目录
类与对象
类与对象的区别和联系:
1、类是抽象的,概念的,代表一类事物,是数据类型。
2、对象是具体的,实际的,代表一个具体事物,即实例。
3、类是对象的模板,对象是类的一个个体,对应一个实例。
属性/成员变量:
1、成员变量=属性=field(字段),成员变量是用来表示属性的。
2、属性是类的一个组成部分,一般是基本数据类型,也可是引用类型(对象,数组)。
class Car{
String name;
String[] master;
}
注意事项和细节说明:
1、属性的定义语法:访问修饰符 属性类型 属性名;
有四种访问修饰符:public,proctected,默认,private。
2、属性的定义类型可以为任意类型,包含基本类型或引用类型。
3、属性如果不赋值,有默认值,规则和数组一致:
int 0,shot 0,byte 0,long 0,float 0.0,double 0.0,char \u0000, boolean false,String null.
public static void main(String[] args) {
//创建Person对象
//p1 是对象名(对象引用)
//new Person()创建的对象空间(数据)才是真正的对象
Person p1= new Person();
}
class Person {
int age;
}
创建对象
1、先声明再创建
Cat cat;//声明对象cat
cat=new Cat();//创建
2、直接创建
Cat cat =new Cat();
访问属性
对象名.属性名
cat.name;
类和对象的内存分配机制
Person p1=new Person();
p1.age=10;
p1.name='小明';
Person p2=p1;//把p1赋给了p2,让p2指向p1
System.out.println(p2.age);//10
Java内存的结构分析:
1、栈:存放基本数据类型(局部变量)
2、堆:存放对象(Cat cat、数组等)
3、方法区:常量池(常量,比如字符串),类加载信息
java创建对象的流程简单分析:
Person p=new Person();
p.name="jack";
p.age=10;
1、先加载Person类信息(属性和方法信息,只会加载一次)
2、在堆中分配空间,进行默认初始化(看规则)
3、把地址赋给p,p就指向对象
4、进行指定初始化,比如p.name="jack'' p.age=10
Person a=new Person();
a.age=10;
a.name="小明";
Person b;
b=a;
System.out.println(b.name);//小明
b.age=200;
b=null;
System.out.println(a.age);//200
System.out.println(b.age);//异常
成员方法
1、成员方法(简称方法) 比如人类除了一些属性外,还有一些行为,这些行为用成员方法才能完成。
public class Method01 {
public static void main(String[] args) {
//方法使用:
//1、方法写好后,要调用(使用)才能输出
//2、先创建对象,然后调用方法即可
Person p1=new Person();
p1.speak();//调用方法
p1.cal01();
p1.cal02(5);
p1.cal02(10);
//把方法getSum返回的值,赋给变量returnRes
int returnRes=p1.getSum(10,20);
System.out.println("getSum方法返回的值="+returnRes);
}
}
class Person {
String name;
int age;
//方法
//添加speak成员方法
//1、public表示公开
//2、void:表示方法没有返回值
//3、speak():speak是方法名,()为形参列表
//4、{}是方法体,写要执行的代码
//5、System.out.println("我是一个好人");表示我们的方法就是输出一句话
public void speak() {
System.out.println("我是一个好人");
}
//添加cal01方法,计算从1+..+1000的结果
public void cal01(){
int res =0;
for(int i=1;i<=1000;i++){
res +=i;
}
System.out.println("计算结果="+res);
}
//该方法可以接收一个数n,计算从1+..n的结果
//1、(int n)形参列表,表示当前有一个形参n,可以接收用户输入。
public void cal02(int n){
int res =0;
for(int i=1;i<=n;i++){
res +=i;
}
System.out.println("计算结果="+res);
//可以计算两个数的和
//1、int:表示方法执行后,返回一个int值
//2、形参列表,2个形参
//3、return res;表示把res的值,返回
public int getSum(int num1,int num2){
int res=num1+num2;
return res;
}
}
方法调用小结
1、当程序执行到方法时,就会开辟一个独立的空间(栈空间)
2、当方法执行完毕,或执行到return语句时,就会返回
3、返回到调用方法的地方
4、返回后,继续执行方法后面的代码
5、当main方法(栈)执行完毕,整个程序退出
//main方法
int[][]map={{0,0,1},{1,1,1},{1,1,3}};
MyTools tool=new MyTools();
//使用方法
tool.printArr(map);
class MyTools {
//方法,接收一个二维数组
public void printArr(int[][] map) {
//对传入的map数组进行遍历输出
for(int i=0;i<map.length;i++) {
for(int j=0;j<map[i].length;j++){
System.out.print(map[i][j]+"");
}
}
}
}
成员方法的好处:提高代码的复用性;可以将实现的细节封装起来,然后供其他用户来调用即可。
成员方法的定义
访问修饰符 返回数据类型 方法名(形参列表){//方法体
语句;
return 返回值;
}
1、形参列表:表示成员方法输入cal(int n)
2、返回数据类型:表示成员方法输出,void表示没有返回值
3、方法主体:表示为了实现某一功能代码块
4、return语句不是必须的(例如 void)
方法注意事项和使用细节1
1、访问修饰符(作用控制方法使用范围)
不写则默认访问,有四种:public,protected,默认,private
2、返回数据类型:
①、一个方法最多有一个返回值,如果返回多个结果,则返回数组。
//main方法
AA a=new AA();
int[] res =a.getSumAndSub(1,4);
System.out.println("和="+res[0]);
System.out.println("差="+res[1]);
class AA{
public int[] getSumAndSub(int n1,int n2) {
int[] resArr =new int[2];//创建一个数组
resArr[0]=n1+n2;
resArr[1]=n1-n2;
return resArr;
}
}
②、返回类型可以为任意类型,包含基本类型和引用类型(数组,对象)
③、若方法要求有返回数据类型,则方法体中最后的执行语句必须为return值;而且要求返回值类型必须和return的值类型一致或兼容
public double f1() {
double d1=1.1*3;
int n=100;
return n;//int->double 而double->int(×)
}
public double f2() {
double d1=1.1*3;
return 1.1;//int->double
}
④、方法是void,则方法体中可以没有return语句,或者只写return;
public void f2(){
System.out.println("hello1");
return;
}
⑤、方法名
遵守驼峰命名法,最好见名知义,表达出该功能的意思。
注意事项和使用细节2
形参列表
①、一个方法可以有0个参数,也可以有多个,用逗号隔开。
②、参数类型可以为任意类型(基本类型/引用类型)。
③、调用带参数的方法时,对应着参数列表传入相同类型或兼容类型的参数。
④、方法定义时的参数称为形式参数,简称形参;方法调用时的参数称为实际参数,简称实参。
实参和形参的类型要一致或兼容、个数、顺序必须一致。
方法体:里面写完成功能的具体的语句,可以为输入、输出、变量、运算、分支、循环、方法调用,当里面不能再定义方法。即方法不能嵌套定义!
注意事项和使用细节3
方法调用细节
①、同一个类中的方法调用:直接调用即可。
//main方法
A a=new A();
a.sayOk();
class A {
public void print(int n){
System.out.println("print()方法被调用 n="+n);
}
public void sayOk() {//sayOk调用print(直接调用即可)
print(10);
System.out.println("继续执行sayOk()");
}
//输出结果为:
//print()方法被调用 n=10
//继续执行sayOk()
②、跨类中的方法A类调用B类方法:需要通过对象名调用。
//main方法
A a=new A();
a.sayOk();
class A {
public void print(int n){
System.out.println("print()方法被调用 n="+n);
}
public void sayOk() {//sayOk调用print(直接调用即可)
print(10);
System.out.println("继续执行sayOk()");
}
public void m1(){
System.out.println("m1()方法被调用");//先输出这句话
B b=new B();
b.hi();
System.out.println("m1()继续执行");//最后输出这句话
}
}
class B {
pubic void hi(){
System.out.println("B类中的hi()被执行");//然后输出这句话
}
}
③、跨类的方法调用和方法的访问修饰符相关。
小总结
类定义的完善:
class 类名{
属性(成员变量);
成员方法;
}
成员方法传参机制
public static void main(String[] args) {
int a=10;//实参
int b=20;
AA obj=new AA();
obj.swap(a,b);//调用swap
System.out.println("main 方法a="+a"b="+b);//③10 20 主栈的a和b没发生交换
}
}
class AA{
public void swap(int a,int b){//swap栈的变化不影响主栈
System.out.println("\n交换前\na="+a+"\tb="+b);//①10 20
//完成a和b的交换
int tmp=a;
a=b;
b=tmp;
System.out.println("\n交换后\na="+a+"\tb="+b);//②20 10
}
}
结论:基本数据类型,传递的是值(值拷贝),形参的任何改变不影响实参。
引用数据类型的传参机制
public class MethodParameter02{
public static void main(String[] args){
B b=new B();
int[] arr={1,2,3};
b.test100(arr);
//遍历数组
System.out.println("main的arr数组");
for(int i=0;i<arr.length;i++){
System.out.print(arr[i]+"\t");//② 200 2 3
}
System.out.println();
}
}
class B {
public void test100(int[]arr){
arr[0]=200;//修改元素
//遍历数组
System.out.println("text100的 arr数组");
for(int i=0;i<arr.length;i++){
System.out.print(arr[i]+"\t");//① 200 2 3
}
System.out.println();
}
结论:引用类型传递的是地址(传递也是值,但是值是地址),可以通过形参影响实参!
//main方法
Person p =new Person();
B b=new B();
p.nae="jack";
p.age=10;
b.text200(p);//p是引用 存的是对象的地址
System.out.println("main 的p.age="+p.age);//10000
}
}
class Person{
String name;
int age;
}
class B{
public void text200(Person p){
p.age=10000;
}
Person p=new Person();
B b=new B();
p.age=10;
b.text200(p);
System.out.println("main 的p.age="+p.age);//10
}
}
class person{
String name;
int age;
}
class B{
public void text200(Person p){
p=null;//指向空
}
Person p=new Person();
p.age=10;
b.text200(p);
System.out.println("main 的p.age="+p.age);//10 main里的p仍是10
}
}
class person{
String name;
int age;
}
class B{
public void text200(Person p){
p= new Person();//新建了一个新对象 指向新对象 这个对象会被当做垃圾回收
p.name="tom";
p.age=99;
}
克隆对象
//main函数
Person p=new Person();
p.name="milan";
p.age=100;
//创建tools
MyTools tools =new MyTools();
Person p2=tools.copyPerson(p);
//到此p和p2是Person对象,但是两个独立的对象,属性相同
System.out.println("p的属性 age="+p.age+"名字="+p.name);
System.out.println("p2的属性 age="+p2.age+"名字="+p2.name);
//可以通过输出对象的hashCode看看对象是否是同一个
System.out.println(p==p2);//false
class Person {
String name;
int age;
}
class MyTools {
public Person copyPerson(Person p) {
//创建一个新对象
Person p2=new Person();
p2.name= p.name;//把原来对象的名字赋给p2.name
p2.age=p.age;
return p2;
}
递归
递归就是方法自己调用自己,每次调用时传入不同的变量。
递归(recursion)执行机制
1、打印问题
//main方法
T t1=new T();
t1.test(4);//输出n=2 n=3 n=4
class T{
public void test(int n){
if(n>2){
test(n-1);
}
Systm.out.println("n="+n);
}
}
//方法执行完就回到被调用的语句
//main方法
T t1=new T();
t1.test(4);//只输出n=2
class T{
public void test(int n){
if(n>2){
test(n-1);
}else{
Systm.out.println("n="+n);
}
}
每调用一次方法就新开一个栈
2、阶乘问题
T t1=new T();
int res=t1.factorial(5);
System.out.println("res="+res);//120
}
}
class T{
public int factorial(int n){
if(n==1) {
return 1;
}else{
return factorial(n-1)*n;
}
递归总是从顶级的栈开始返回
递归重要规则
1、执行一个方法时,就创建一个新的受保护的独立空间(栈空间)。
2、方法的局部变量是独立的,不会相互影响,比如n变量。
3、如果方法中使用的是引用类型变量(比如数组,对象),就会共享该引用类型的数据。
4、递归必须向退出递归的条件逼近,否则就是无限递归,出现StackOverflowError,栈溢出。
5、当一个方法执行完毕,或者遇到return,就会返回,遵守谁调用,就将结果返回给谁,同时当方法执行完毕或者返回时,该方法也就执行完毕(栈释放)。
方法重载(OverLoad)
java中允许同一个类中,多个同名方法的存在,但要求形参列表不一致。
例如:System.out.println();out是PrintStream类型
public class OverLoad01 {
public static void main(String[] args){
System.out.println(100);
System.out.println("hello,world");
System.out.println('h');
System.out.println(1.1);
System.out.println(true);
}
}
MyCalculator mc=new MyCalculator();
System.out.println(mc.calculate(1,2));
System.out.println(mc.calculate(1.1,2));
}
}
class MyCalculator {
//下面的四个calculate方法构成了重载
public int calculate(int n1,int n2) {
return n1+n2;
}
public int calculate(int n1,double n2) {
return n1+n2;
} public int calculate(double n1,int n2) {
return n1+n2;
}
public int calculate(int n1,int n2,int n3) {
return n1+n2+n3;
}
}
注意事项和使用细节
1、方法名:必须相同。
2、形参列表:必须不同(形参类型/个数/顺序,至少有一样不同,参数名无要求)。
class MyCalculator {
//不构成重载,是方法的重复定义
public int calculate(int n1,int n2) {
return n1+n2;
}
public int calculate(int a1,int a2) {
return a1+a2;
}}
3、返回类型:无要求。
class MyCalculator {
//不构成重载,是方法的重复定义
public int calculate(int n1,int n2) {
return n1+n2;
}
public void calculate(int n1,int n2) {
int res=n1+n2;
}}
可变参数(variable parameters)
概念:java允许将同一个类中多个同名同功能但参数个数不同的方法,封装成一个方法。就可以通过可变参数实现。
基本语法:
访问修饰符 返回类型 方法名(数据类型...形参名){
}
public static void main(String[] args){
HspMethod m=new HspMethod;
System.out.println(m.sum(1,5,100));//106
System.out.println(m.sum(1,19));//20
class HspMethod {
//1、int...表示接受的是可变参数,类型是int,即可以接收多个int(0-多)
//2、使用可变参数时,可以当作数组来使用,nums可以当作数组
//3、遍历nums求和即可
public int sum(int... nums){
// System.out.println("接收的参数个数="+nums.length);//3
int res=0;
for(int i=0;i<nums.length;i++) {
res+=nums[i];
}
return res;
}
注意事项和细节
1、可变参数的实参可以为0个或任意多个。
2、可变参数的实参可以为数组。
public class VarParameterDetail {
public static void main(String[] args) {
int[] arr={1,2,3};
T t1=new T();
t1.f1(arr);
}
}
class T{
public void f1(int... nums){
System.oout.println("长度="+nums.length);//3
}
}
3、可变参数的本质就是数组。
4、可变参数可以和普通类型的参数一起放在形参列表,但必须保证可变参数在最后。
public void f2(String str,double... nums) {
}
5、一个形参列表只能有一个可变参数。
例子:
public class VarParameterExercise {
public static void main(String[] args){
HspMethod hm =new HspMethod();
System.out.println(hm.showScore("milan",90.1,80.0));
System.out.println(hm.showScore("milan",90.1,80.0,70.0,70,30.5));
}
}
class HspMethod {
public String showScore(String name,double... scores){
double totalScore=0;
for(int i=0;i<scores.length;i++){
totalScore+=score[i];
}
return name+"有"+scores.length+"门课成绩总分为="+totalScore;
}
}
作用域(scope)
1、在java编程中,主要的变量就是属性(成员变量)和局部变量。
2、局部变量一般指在成员方法中定义的变量。
3、java中作用域的分类
全局变量:也就是属性,作用域为整个类体
局部变量:就是除了属性之外的其它变量,作用域为定义它的代码块中。
4、全局变量可以不赋值,直接使用,因为有默认值,局部变量必须赋值后,才能使用,因为没有默认值。
public class VarScope {
public static void main(String[] args){
}
}
class Cat {
//属性在定义时,可以直接赋值
int age=10;
double weight;//默认值是0.0
{
int num = 100;
}
public void cry(){
//n和name就是局部变量,局部变量必须赋值后,才能使用,没有默认值。
//n和name的作用域在 cry方法中
int n=10;
String name="jack";
System.out.println("在cry中使用属性 age="+ age);
}
public void eat(){
System.out.println("在eat中使用属性 age="+age);
}
注意事项和细节使用
1、属性和局部变量可以重名,访问时遵循就近原则。
//main方法
Person p1=new Person();
p1.say();
}
}
class Person {
String name="jack";
public void say(){
String name="king';
System.out.println("say()name="+name);//king
}
}
2、在同一个作用域中,比如在同一个成员方法中,两个局部变量,不能重名。
3、属性生命周期较长,伴随着对象的创建而创建/销毁而销毁。
局部变量生命周期较短,伴随着它的代码块的执行而创建/结束而销毁,即在一次方法调用过程中。
例如:当执行say方法时,say方法的局部变量比如name会创建,当say执行完毕后name局部变量就销毁,但属性(全局变量)仍然可以使用。
4、作用域范围不同
全局变量/属性:可以被本类使用,或其它类使用(通过对象调用)。
局部变量:只能在本类中对应的方法中使用。
例:两种方式
//main方法
Person p1=new Person();
T t1=new T();
t1.test();
}
}
class T{
//全局变量/属性:可以被本类使用,或其它类使用(通过对象调用)。
public void test(){
System.out.println(p1.name);//jack
}
class Person {
String name="jack";
public void say(){
}
}
}
//main方法
Person p1=new Person();
T t1=new T();
t1.test2(p1);
}
}
class T{
//全局变量/属性:可以被本类使用,或其它类使用(通过对象调用)。
public void test2(Person p) {
System.out.println(p.name);//jack
}
class Person {
String name="jack";
public void say(){
}
}
}
5、修饰符不同
全部变量/属性可以加修饰符
局部变量不可以加修饰符
class Person {
private int age =20;//(属性)
public void say(){
String name="jack";//(局部变量)
}
}
构造器
例如:在创建人类的对象时,就直接指定这个对象的年龄和姓名。
基本语法:
【修饰符】方法名(形参列表){
方法体;
}
1、构造器的修饰符可以默认,也可以是public,protected,private。
2、构造器没有返回值。
3、方法名和类名字必须一样。
4、参数列表和成员方法一样的规则。
5、构造器的调用由系统完成。
基本介绍:
构造方法又叫构造器(constructor),是类的一种特殊的方法,主要作用:完成对新对象的初始化。(并不是去创建对象,对象的空间已经有了,具体的属性的值的初始化由构造器完成)
特点:
1、方法名和类名相同。
2、没有返回值。
3、在创建对象时,系统会自动的调用该类的构造器完成对对象的初始化。
public class Constuctor01{
public static void main(String[] args){
//当我们new一个对象时,直接通过构造器指定名字和年龄
Person p1= new Person("smith",80);
System.out.println("p1的信息如下:");//②
System.out.println("p1对象name=:"+p1.name);//③smith
System.out.println("p1对象age=:"+p1.age);//④80
}
}
class Person {
String name;
int age;
//构造器
//1、构造器没有返回值,也不能写void
//2、构造器的名称和类Person一样
//3、(String pName,int pAge)是构造器形参列表,规则和成员方法一样
public Person(String pName,int pAge){
System.out.println("构造器被调用,完成对象的属性初始化");//①
name=pName;
age=pAge;
}
}
注意事项和使用细节
1、一个类可以定义多个不同的构造器,即构造器重载。
Person p1=new Person("king",40);
Person p2=new Person("tom");
}
}
class Person{
String name;
int age;//默认0
//第一个构造器
public Person(String pName,int pAge){
name=pName;
age=pAge;
}
//第二个构造器
public Person(String pName) {
name=pName;
}
}
2、构造器名和类名要相同。
3、构造器没有返回值。
4、构造器是完成对象的初始化,并不是创建对象。
5、在创建对象时,系统自动的调用该类的构造方法。
6、如果程序员没有定义构造方法,系统会自动给类生成一个默认无参构造器(也叫默认构造器)。
Dog dog1= new Dog();
}
}
class Dog{
/*默认构造器
Dog(){
}
*/
}
7、一旦定义了自己的构造器,默认的构造器就覆盖了,就不能再使用默认的无参构造器,除非显式的定义一下,即Dog(){}。
Person p1=new Person();
System.out.println("p1的信息 name="+p1.name +"age="+p1.age);//name=null; age=18;
Person p2=new Person("scott",50);
System.out.println("p2的信息 name="+p2.name +"age="+p2.age);//name=scott; age=50;
}
}
class Person {
String name;
int age;
public Person(){//无参构造器
age=18;
}
public Person(String pName,int pAge) {//带两个参数的构造器
name=pName;
age=pAge;
}
}
对象创建的流程分析
class Person{
int age=90;
String name;
Person(String n,int a){//构造器
name=n;//把传进去的两个形参赋值给属性
age=a;
}}
Person p=new Person("小亮",20);
对象在堆里面
p只能称为对象的引用/名字
·流程分析:
1、加载Person类信息(Person.class),只会加载一次。
2、在堆中分配空间(地址)。
3、完成对象初始化(3.1默认初始化age=0 name=null 3.2显式初始化age=90 name=null 3.3构造器初始化 age=20 name=小亮)。
4、在对象在堆中的地址,返回给p。
小总结
class 类名{
成员变量;
构造器;
成员方法;
}
this关键字
java虚拟机会给每个对象分配this,代表当前对象。
Dog dog1 =new Dog("大壮",3);
dog1.into();
Dog dog2 =new Dog("大黄",2);
dog2.into();
class Dog{
String name;
int age;
public Dog(String name,int age){//构造器
//this.name 当前对象的属性name
this.name=name;
this.age=age;
}
public void into(){//成员方法
System.out.println(name +"\t" +age +"\t");//大壮 3 大黄 2
}
}
this小结:哪个对象调用,this就代表哪个对象。
注意事项和细节
1、this关键字可以用来访问本类的属性、方法、构造器。
2、this用于区分当前类的属性和局部变量。
3、访问成员方法语法:this.方法名(参数列表);
//main方法
T t1=new T();
t1.f2();
class T {
public void f1(){
System.out.println("f1()方法..");
}
public void f2(){
System.out.println("f2()方法..");
//调用本类的f1
//法1
f1();
//法2
this.f1();
}
4、访问构造器语法:this(参数列表);注意只能在构造器中使用(即只能在构造器中访问另外一个构造器)。
访问构造器语法:this(参数列表);必须放置第一条语句。
//main方法
T t2=new T();
class T {
public T(){
//这里去访问T(String name,int age)
//访问构造器语法:this(参数列表);必须放置第一条语句
this("jack",100);
System.out.println("T()构造器");//②
}
public T(String name,int age){
System.out.println("T(String name,int age)构造器");//①
}
}
5、this不能在类定义的外部使用,只能在类定义的方法中使用。
//main
T t2=new T();
t2.f3();
class T{
String name="jack";
int num=100;
public void f3(){
String name="smith";
System.out.println("name="+name +"num="+num);//若有局部变量要访问局部变量 smith 100
//也可以这样
System.out.println("name="+this.name +"num="+this.num);//准确的访问属性 jack 100
}
}