以下为本人观看尚硅谷Java学习视频所整理的学习笔记
目录
继承性
单词extends的含义:延展、拓展
继承性的好处:
1、减少了代码的冗余,提高了代码的复用性
2、便于功能的扩展
3、为之后多态性的使用,提供了前提
继承性的格式:class A extends B {}
A:子类、派生类、subclass
B:父类、超类、基类、superclass
一旦子类A继承父类B以后,子类A中就获取了父类B中声明的所有属性和方法
特别的,父类中声明为private的属性或方法,子类继承父类以后,仍然认为获取了父类中私有的结构。
只有因为封装性的影响,使得子类不能直接调用父类的结构而已。
子类继承父类以后,还可以声明自己特有的属性或方法,实现功能的拓展
子类和父类的关系,不同于子集和集合的关系(子类比父类功能更强大)
Java只支持单继承和多层继承,不允许多重继承
- 一个子类只能有一个父类(Java具有单继承性,不同于C++)
- 一个父类可以派生出多个子类
- 子父类是相对的概念
- 子类直接继承的父类称为直接父类;间接继承的父类称为间接父类
- 子类继承父类之后,获取了直接父类以及所有间接父类中声明的属性和方法
例:class A extends B, C, D… //错误
如果我们没有显式地声明一个类的父类的话,则此类继承于java.lang.Object类
所有的java类(除java.lang.Object类之外)都直接或间接地继承于java.lang.Object类
意味着,所有的java类都具有java.lang.Object类声明的功能
方法的重写
重写:override/overwrite
定义:在子类中可以根据需要对父类中继承来的方法进行改造,也称为方法的重置、覆盖。在程序执行时,子类的方法也将覆盖父类的方法
要求:
1、子类重写的方法必须和父类被重写的方法具有相同的方法名称、参数列表
2、子类重写的方法的返回值类型不能大于父类被重写的方法的返回值类型
- 父类被重写的方法的返回值是void,则子类重写的方法的返回值类型也只能是void
- 父类被重写的方法的返回值类型是A类型,则子类重写的方法的返回值类型可以是A类或A类的子类
- 父类被重写的方法的返回值类型是基本数据类型,则子类重写的方法的返回值类型必须是相同的基本数据类型
3、子类重写的方法使用的访问权限不能小于父类被重写的方法等访问权限
- 子类不能重写父类中声明为private权限的方法
4、子类方法抛出的异常不能大于父类被重写方法的异常
注意:
子类与父类中同名同参数的方法必须同时声明为非static的(即为重写),或者同时声明为static的(不是重写)。因为static方法是属于类的,子类无法覆盖父类的方法。
面试题:区分方法的重载和重写
四种访问权限修饰符
Java权限修饰符public、protected、(缺省)、private置于类的成员定义前,用来限定对象对该类成员的访问权限。
修饰符 | 类内部 | 同一个包 | 不同包的子类 | 同一个工程 |
private | Yes | |||
(缺省) | Yes | Yes | ||
protected | Yes | Yes | Yes | |
public | Yes | Yes | Yes | Yes |
对于class的权限修饰只可以用public和default(缺省)
- public类可以在任意地方被访问
- default类只可以被同一个包内部的类访问
super
super关键字的使用:
1、super理解为:父类的
2、super可以用来调用:属性、方法、构造器
3、super的使用:调用属性和方法
- 我们可以在子类的方法或构造器中,通过使用“super.属性”或“super.方法”的方式,显式地调用父类中声明的属性或方法。但是,通常情况下,我们习惯省略“super.”
- 特殊情况:当子类和父类中定义了同名的属性或方法时,我们要想在子类中调用父类中声明的属性或方法,则必须显式地使用“super.属性”或“super.方法”的方式,表明调用的是父类中声明的属性或方法
4、super的使用:调用构造器
- 我们可以在子类的构造器中显式地使用“super(形参列表)”的方式调用父类中声明的指定构造器
- “super(形参列表)”的使用必须声明在子类构造器的首行
- 我们在类的构造器中,针对于“this(形参列表)”或“super(形参列表)”只能二选一,不能同时出现
- 当构造器的首行中,没有显式地声明“this(形参列表)”或“super(形参列表)”,则默认调用的是父类中空参的构造器
- 在类的多个构造器中,至少有一个类的构造器中使用了“super(形参列表)”,调用父类中的构造器
子类对象实例化过程
子类对象实例化的全过程
1、从结果上来看:
子类继承父类以后,就获取了父类中声明的属性和方法
创建子类的对象,在堆空间中,就会加载所有父类中声明的属性
2、从过程上来看:
当我们通过子类的构造器创建子类对象时,我们一定会直接或间接地调用其父类的构造器,进而调用父类的父类的构造器,直到调用了java.lang.Object类中空参的构造器为止。正因为加载过所有的父类的结构,所以才可以看到内存中有父类中的结构,子类对象才可以考虑进行调用。
3、明确:虽然创建子类对象时,调用了父类的构造器,但是自始至终就创建过一个对象,即为new的子类对象
多态性
多态性,是面向对象中最重要的概念,在Java中的体现:
对象的多态性:父类的引用指向子类的对象
- 可以直接应用在抽象类和接口上
Java引用变量有两个类型:编译时类型和运行时类型。编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。简称:编译时,看左边;运行时,看右边
- 若编译时类型和运行时类型不一致,就出现了对象的多态性(Polymorphism)
- 多态情况下,“看左边”:看的是父类的引用(父类中不具备子类特有的方法);“看右边”:看的是子类的对象(实际运行的是子类重写父类的方法)
理解多态性:可以理解为一个事物的多种形态
对象的多态性:父类的引用指向子类的对象(或者说子类的对象赋给父类的引用)
Person p2 = new Man(); //假设Man类是Person类的子类
多态的使用:虚拟方法调用
有了对象的多态性以后,我们在编译期,只能调用父类中声明的方法,但在运行期,我们实际执行的是父类子类重写父类的方法。(编译时,看左边;运行时,看右边)
当调用子父类同名同参的方法时,实际执行的是子类重写父类的方法----虚拟方法的调用。
多态性的使用前提:
① 类的继承关系
② 方法的重写
对象的多态性,只适用于方法,不适用于属性(编译和运行都看左边)
也就是说如果子父类有相同的属性名,通过父类的引用调用该属性名,调用的是父类中的属性
虚拟方法调用(多态情况下)
子类中定义了与父类同名同参数的方法,在多态情况下,将此时父类的方法称为虚拟方法,父类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译期是无法确定的。
对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法,这称为“早绑定”或“动态绑定”
而对于多态,只有等到方法调用的那一刻,解释编译器才会确定所要调用的具体方法,这称为“晚绑定”或“动态绑定”
有了对象的多态性后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致编译时,只能调用父类中声明的属性和方法。子类特有的属性和方法不能调用。
instanceof关键字
向下转型:使用强制类型转换符
使用强制转换时,可能出现ClassCastException的异常
例如Man和Woman都是Person的子类,那么以下代码:
Person p1 = new Man();
Woman w1 = (Woman)p1;
就会出现ClassCastException的异常
x instanceof A:检验x是否为类A的对象,返回值为boolean型
- 要求x所属的类与类A必须是子类和父类的关系,否则编译错误
- 如果x属于类A的子类B,x instanceof A值也为true
使用情景:为了避免在向下转型时出现ClassCastException的异常,我们在向下转型之前,先进行instanceof的判断,一旦返回true,就进行向下转型;如果返回false,就不进行向下转型
例:
if(p1 instance of Woman){
Woman w1 = (Woman)p2;
}
设类B是类A的父类,如果a instanceof A返回true,则a instanceof B也返回true
Object类的使用
Oject类是所有Java类的根父亲
如果在类的声明中未使用extends关键字指明其父类,则默认父类为java.lang.Object类
Object类中的功能(属性和方法)具有通用性
属性:无
方法:equals() / toString() / getClass() / hashCode() / clone() / finalize() / wait() / notify() / notifyAll()
Object类只声明了一个空参的构造器
面试题:final、finally、finalize的区别
面试题:== 和 equals() 的区别
== :运算符
1、可以使用在基本数据类型变量和引用数据类型变量中
2、如果比较的是基本数据变量:比较两个变量保存的数据是否相等(类型不同也返回true);如果比较的是引用数据类型变量:比较两个对象的地址值是否相同
equals()方法的使用:
1、这是一个方法,不是运算符
2、只能适用于引用数据类型
3、Object类中equals()的定义:
public boolean equals(Object obj){
return (this == obj);
}
说明:Object类中定义的equals()和==的作用是相同的:比较两个对象的地址值是否相同,即两个引用是否指向同一个对象实体
4、像String、Date、File、包装类等都重写了Object类中的equals()方法,重写以后,比较的不是两个引用的地址是否相同,而是比较两个对象的“实体内容”是否相同
5、通常情况下,我们自定义的类如果使用equals()的话,也通常是比较两个对象的“实体内容”是否相同。那么,我们就需要对Object类中的equals()进行重写(重写原则:比较两个对象的实体内容是否相同)(一般IDE也会提供自动生成比较属性是否相同的equals方法功能)
Object类中toString()的使用:
1、当我们输出一个对象的引用时,实际上就是调用了当前对象的toString()方法
2、Object类中toString()的定义:
public Stirng toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
3、像String、Date、File、包装类等都重写了Object类中的toString()方法,使得在调用对象的toString()时,返回“实体内容”信息
4、自定义类也可以重写toString()方法,当调用此方法时,返回对象的“实体内容”
包装类的使用
Java中的JUnit单元测试
步骤:
1、选中当前工程 - 右键选择:build path - add libraries - Junit 5 - 下一步
2、创建Java类,进行单元测试
此时的Java类要求:①此类是public的 ②此类提供公共的无参的构造器
3、此类中声明单元测试方法
此时的单元测试方法:方法的权限是public,没有返回值,没有形参
4、此单元测试方法上需要声明注解:@Test,并在单元测试类中导入:import org.junit.Test;
5、声明好单元测试方法以后,就可以在方法体内测试相关的代码
6、写完代码以后,左键双击单元测试方法名,右键:run as - Junit Test
说明:
1、如果执行结果没有任何异常:绿条
2、如果执行结果出现异常:红条
包装类(Wrapper)
针对八种数据类型定义相应的引用类型——包装类(封装类)
有了类的特点,就可以调用类中的方法,Java才是真正的面向对象
基本数据类型包装成包装类的实例 --- 装箱
- 通过包装类的构造器实现:
int i = 500; Integer t = new Integer(i);
- 还可以通过字符串参数构造包装类对象
Float f = new Float("4.56");
Long l = new Long("asdf"); //NumberFormatException
获得包装类对象中包装的基本类型变量 ---拆箱
- 调用包装类的.xxxValue()方法
boolean b = bObj.booleanValue();
JDK1.5之后,支持自动装箱和自动拆箱,但类型必须匹配
掌握:基本数据类型、包装类、String三者之间的相互转换
基本数据类型 --> 包装类:调用包装类的构造器
int num1 = 10;
Integer in1 = new Integer(num1);
包装类 --> 基本数据类型:调用包装类的xxxValue()
Integer in1 = new Integer(12);
int num1 = in1.intValue();
JDK 5.0 新特性:自动装箱与自动拆箱
//自动装箱:基本数据类型 --> 包装类
int num1 = 10;
integer in1 = num1;
//自动拆箱:包装类 --> 基本数据类型
Integer in1 = new Integer(10);
int num1 = in1;
String类型 --> 基本数据类型、包装类:调用包装类的parseXxx()
String str = "123";
int num = (int)str; //错误
Integer in1 = (integer)str; //错误
int num = Integer.parseInt(str); //正确
基本数据类型、包装类 --> String类型:调用String重载的valueOf(Xxx xxx)
方式1:连接运算
int num = 10;
String str1 = num + "";
方式2:调用String的valueOf(Xxx xxx)
float f = 12.3f;
String str2 = String.valueOf(f);
注意点:
boolean默认初始化值是false,Boolean默认初始化值是null
Boolean包装类中对于true的判断:只有字符串的内容等于"true"(不缺分大小写,也就是"tuRe"也是true)才是true,其他情况全部都是false
面试题:
integer i = new Integer(1);
integer j = new Integer(1);
那么( i == j )值为false,因为引用存储的地址值不同
注意:
Integer内部定义了IntegerCache结构,IntegerCache中定义了Integer[],保存了从-128~127范围的整数,如果我们使用自动装箱的方式,给Integer赋值的范围在-128~127范围内,可以直接使用数组中的元素,不用再去new。目的是为了提高效率
所以:
integer m = 1;
integer n = 1;
那么( m == n )值为true
integer m = 128;
integer n = 128;
那么( m == n )值为false
面试题:
Object o1 = true? new Integer(1) : new Double(2.0);
输出o1,值为1.0
因为三目运算符中后续两个表达式需要类型相同,Integer被升级为Double
Object o2;
if (true) o2 = new Integer(1);
else o2 = new Double(2.0);
输出o2,值为1