目录
类及类的成员
1、属性=成员变量=字段=域=instance
属性可以在创建类的对象后手动初始化也可以直接在定义时初始化也可以使用它的默认初始化值(默认初始化值同数组);
2、方法=函数=method
类和类的对象的使用:
1、创建类:包括创建属性,方法,构造器等等;
2、实例化类=创建类的对象:new + 构造器;
3、通过类的对象来调用类的成员:“对象名.属性名”和“对象名.方法名”;
▲要深刻理解类,属性,方法调用之间的关系:要想调用一个方法/属性,只能通过该方法所在类的对象来调用。
▲同一个类的不同对象拥有独立的完整属性(除了static属性),修改一个对象的属性不影响另一个的属性。
▲类也是一种数据类型,和数组一样是引用类型,所以类的对象直接赋值赋的是地址值,两者调用的属性和方法是指向同一个堆空间。
类的对象的内存解析:
直接打印变量名输出的是地址值,直接赋值变量名给另一个变量也是地址值。
类的数组对象的内存解析:
类的匿名对象:
匿名对象是新建的类的对象没有赋值给一个变量名,new Person().方法()
▲匿名对象只能调用一次,再次new就会new一个新的对象。
▲具体使用时,可能会在显式的调用方法时将匿名对象作为形参使用(如果该方法的形参是自定义类对象的话),这时候在方法内部匿名对象跟形参一样可能可以执行多次。
成员变量(属性)和局部变量
声明位置:
成员变量(属性)直接在类中声明;
局部变量在方法中,方法形参中,代码块中,构造器形参,构造器内部中声明。
权限修饰符:
成员变量(属性)可以用private,默认,protected,public等权限修饰符修饰;
局部变量不可以用修饰符修饰。
默认初始化值:
成员变量(属性)有默认初始化值(同数组);
局部变量没有默认初始化值,必须手动赋值,对形参必须调用时赋值。(数组变量跟其他变量不一样,虽然它也是局部变量,但是它有默认值)
内存位置:
成员变量(属性)在堆中(非static属性)(参考内存解析图,确实在堆中);
局部变量在栈中以类和方法为单位存储。
▲注意,在方法外新建一个对象要看成是这个类的属性!!!!!!
定义方法:
1、权限修饰符 方法返回值类型 方法名(){
方法体;
return 返回值; 或return;(可有可无)
}
2、权限修饰符 方法返回值类型 方法名(形参类型1 形参名1,形参类型2 形参名2……){
方法体;
return 返回值; 或return;(可有可无)
}
▲方法体可以调用当前类的属性和方法,如果调用的是该方法自己,该方法为递归方法。
▲方法体中不能定义方法。
方法的重载:
在同一个类或者其子类中,允许存在一个以上的同名方法,只要它们的参数列表不同。
▲判断是否是重载方法,跟权限修饰符,返回值类型,参数名,方法体都没有关系,只要名字相同,参数个数或者类型或者顺序不同。
可变形参的方法:
1. 声明格式:方法名(参数的类型名...参数名)
2. 可变参数:输入的参数个数是可变多个:0个,1个或多个
3. 可变个数形参的方法与同名同参数类型的方法之间,彼此构成重载。要注意,调用方法时,会优先调用非可变形参的方法。
4. 可变参数方法的使用与方法参数部分使用数组是完全一致的,甚至也可以用数组中的索引调用元素。但数组形参不能和可变形参共存。
5. 方法的参数部分有可变形参,需要放在形参声明的最后
6. 在一个方法的形参位置,最多只能声明一个可变个数形参
方法形参的值传递机制(java中只有值传递机制没有引用传递):
▲首先,调用有参数方法时要把实际参数赋给形式参数,这个过程跟调用它的对象是哪个类的没有关系。
值传递机制问题可以理解成 将main方法里的实参在其他方法里翻来覆去改变了之后,main方法里的实参到底变了没有的问题;所以如果有参方法调用时直接用具体数据当实参,就不存在参数传递问题。
形参:方法声明时的参数;
实参:方法调用时实际赋给形参的参数值;
1、如果形参/实参是基本数据类型或字面量创建的String:将实参的“数据值”传递给形参,外部方法的变量不会改变;
2、如果形参/实参是引用数据类型:将实参引用数据类型变量的“地址值”传递给形参。外部方法的引用变量会改变。
▲注意一点,方法调用完成后,方法内的在栈中的局部变量被回收。
递归方法:
递归方法:一个方法体内调用它自身。
▲方法递归包含了一种隐式的循环,它会重复执行某段代码,但这种重复执行无须循环控制。
▲递归一定要向已知方向递归,否则这种递归就变成了无穷递归,类似于死循环。
详细见习题。
属性的封装与隐藏:
1、问题的引入
2、属性封装性的体现:通过将属性声明为私有的(private),再提供公共的(public)方法:getXxx()和setXxx()实现对该属性的获取和赋值操作,通过调用方法达到调用属性的目的。以实现下述目的:
- 隐藏一个类中不需要对外提供的实现细节;
- 使用者只能通过事先定制好的方法来访问数据,可以方便地加入控制逻辑,限制对属性的不合理操作;
- 便于修改,增强代码的可维护性;
封装性不只体现在属性上,方法,单例模式等等都有其体现。
3、封装性需要权限修饰符配合:
类的内部成员(属性,对象,方法,构造器,内部类)可用private、default、protected、public来修饰。
类只能用default、public修饰
4、通过get和set方法可以让外部结构访问相应封装后的属性。但是注意不要编写返回引用可变对象的访问器方法。
构造方法(构造器)
作用:
1、调用构造器一般是对本类的属性(如果调用构造器前类加载过,则不初始化静态变量)和父类的属性(实例变量和静态变量)进行初始化,而不是初始化对象,初始化对象是通过new关键字实现的
2、通过new调用构造方法初始化对象,编译时根据参数签名来检查构造函数,称为静态联编和编译多态
(参数签名:参数的类型,参数个数和参数顺序)
3、创建子类对象会调用父类构造方法但不会创建父类对象,只是调用父类构造方法完成父类的加载,初始化父类成员属性;
格式:
修饰符名 类名 (参数列表) {
初始化句语句;
}
▲如果类中没有手动的添加构造器,则默认有一个空参构造器,默认构造器的修饰符与所属类的修饰符一致。
一旦手动添加构造器,系统就不会生成空参构造器。
▲类中多个构造器会构成重载。类中至少有一个构造器。
▲父类的构造器不被子类继承。
这个跟后面程序初始化执行顺序一起看。
JavaBean:
JavaBean是指符合如下标准的Java类:
1、类是公共的;2、有一个无参的公共的构造器;3、每个属性有对应的get、set方法。
This关键字:
this用在方法中可以理解为当前对象,用在构造器中可以理解为当前正在创建的对象(this都是用在本类编译过程中,不存在和private冲突的问题)。
1、this用来调用属性或方法:①在类的方法/构造器中,可以用“this.属性名”或“this.方法名”调用本类(若本类没有,会从父类中按就近原则寻找)的属性或方法,但通常在非static方法中省略。②但是当形参名和想要赋给值的属性名相同时,用this.属性名来体现不同,这时的this相当于“当前类的对象”,this.属性名相当于调用该属性。
2、this用来调用构造器:在类的构造器中,可以用“this(形参列表)”来调用本类中其他的重载构造器的初始化语句。“this(形参列表)”要写在本构造器的首行且最多只能声明一个this。
▲形参列表中的参数可以根据其他被重载的构造器选填实际数据。
▲在类的n个构造器中,最多有n-1个this语句。
继承和重写
继承
class A extends B , 继承是is-a的关系,男人和女人都是人。
继承的好处:
1、继承的出现减少了代码冗余,提高了代码的复用性。
2、继承的出现,更有利于功能的扩展。
3、继承的出现让类与类之间产生了关系,提供了多态的前提。
▲子类继承父类的所有属性和方法(包括属性方法前的各种权限修饰符修饰,但private修饰的属性和方法子类不能直接调用,只能通过方法或构造器调用),也可以增加自己特有的属性和方法。
▲一个子类只能有一个父类(但是分直接父类和间接父类),一个父类可以有多个子类。
▲java.lang.Object类是所有类的父类。
方法的重写:
子类对从父类中继承来的方法进行改造,也称为方法的重置、覆盖。
要求:
1.子类重写的方法必须和父类被重写的方法具有相同的方法名称、参数列表
2.子类重写的方法的返回值类型小于等于父类被重写的方法的返回值类型
▲如果父类被重写方法返回值类型为void或基本数据类型,则子类重写方法必须是void或基本数据类型。
▲如果父类被重写方法返回值类型为A,则子类重写方法是A类或A的子类型
3.子类重写的方法使用的访问权限大于等于父类被重写的方法的访问权限
▲子类不能重写父类中声明为private权限的方法
4.子类方法抛出的异常不能大于父类被重写方法的异常
一般对方法的重写直接打方法名根据系统提示自动执行就行。
▲static属性和方法可以被继承,也可以通过子类类名调用,但该操作不会初始化父类。
子类与父类中同名同参数的方法若同时声明为非static的(即为重写),或者同时声明为static的(虽然这时子类该同名同参数static方法看起来也像是重写了父类的同名同参数static方法,但这不是重写,只是子类把父类同名同参数static方法隐藏,只调用自己的同名同参数static方法。因为static方法是属于类的,子类无法覆盖父类的方法)。
Super关键字:
super可以理解为当前类的父类所创建的对象。和this一样,super可以调用父类中(不限于直接父类)声明的属性,方法或构造器。
super调用属性:因为子类继承了父类的属性,这时用this和super效果相同,一般省略this或super,但是在子类中定义了和父类中同名属性时,必须使用super显式调用父类中的属性加以区分。
super调用方法:一般我们都会使用子类中重写的方法,这时相当于省略this,但是在子类中如果想使用父类中被重写的方法时,必须使用super显式调用父类中的方法。
super调用构造方法:在子类的构造器中,可以用“super(形参列表)”来调用父类中相应构造器的初始语句。“super(形参列表)”要写在本构造器的首行且最多只能声明一个super。
▲子类中所有的构造器默认都会访问父类中空参数的构造器,即默认所有子类构造器都有一个super()空参语句。
▲当父类中没有空参数的构造器时, 子类的构造器必须通过this或super(形参列表)语句调用本类或父类的构造器,this和super语句不能同时出现,只能二选一且必须要二选一。
▲在类的构造器中,至少有一个构造器使用了super语句(因为最多有n-1个this语句,所以至少有1个super语句)。
子类对象实例化的过程:
从结果看:子类继承父类的所有属性和方法,创建子类对象时,在堆空间中加载了所有父类的属性。
从过程看:当通过子类构造器创建子类对象时,所以会对所有父类进行加载,直到Object。所以子类内存中有父类的属性和方法。
多态性:
对象的多态性:
父类的引用指向子类的对象。Person p = new Man();
多态的要求:继承/实现和重写。
多态应用:
1、虚拟方法调用
此时若用p调用方法时,在编译过程中,能否编译成功是在将p看成Person类的基础上,如果调用的方法Person中没有,就不能通过编译;在运行过程中,将p看成Man类的对象,执行的是Man类中的重写方法。(注意,这里只谈方法,因为属性和多态性没有关系,即编译和运行都看父类)
2、方法声明的形参类型为父类类型,可以使用子类的对象作为实参调用该方法
关于子父类之间的重载和重写:
子父类之间的重载是在编译完成时编译器即知道类对象调用的是什么方法,属于“早绑定”或“静态绑定”。
子父类之间的重写(多态)是只能在运行时调用方法那一刻运行器才知道对象调用的什么方法,属于“晚绑定”或“动态绑定”。
向上转型和向下转型:
向上转型即为多态,即子类对象赋给父类引用。
向下转型只能用于多态,即已经向上转型后的情况下,本质上就是向上转型,是指当p想在编译期调用Man类特有方法时,需要用强制转换符。Man man = (Man) p ,用man去调用。
向下转型的要求:多态(向上转型),继承(用instanceof判断)
instanceof:判断一个对象是不是指定的类型或其子类型的对象
向下转型有可能失败出异常,例如若 Woman woman = (Woman) p ,此时失败。可以用if(p instanceof Man)语句判断是否可以向下转型。