java学习笔记——第七篇 面向对象之类设计
Java类的继承
类继承语法规则
< 修饰符> class < 子类名称> [extends < 父类>]
{
<属性和方法的声明>
}
知识点
- 子类继承了父类,就继承了父类的方法和属性。(子类对象可以调用父类属性、方法)
- 在子类中,可以使用父类中定义的方法和属性,也可以创建新的数据和方法。因而,子类通常比父类的功能更多,而是对父类的“扩展”。
- 在Java中,继承的关键字用的是“extends”,即子类不是父类的子集,而是对父类的“扩展”。
- Java只支持单继承,不允许多重继承
- 一个子类只能有一个父类
- 一个父类可以派生出多个子类
- 子类不能继承父类中私有的(private)的成员变量和方法,不能直接访问【可以调用父类的public或者protect方法去访问】
访问权限
Java中有四种访问权限,对应的修饰符分别为private,default,protected和public,default就是什么都不加。
- private: Java语言中对访问权限限制的最窄的修饰符,一般称之为“私有的”。被其修饰的类、属性以及方法只能被该类的对象访问,其子类不能访问,更不能允许跨包访问。
- default:即不加任何访问修饰符,通常称为“默认访问模式“。该模式下,只允许在同一个包中进行访问。
- protected: 介于public 和 private 之间的一种访问修饰符,一般称之为“保护形”。被其修饰的类、属性以及方法只能被类本身的方法及子类访问,即使子类在不同的包中也可以访问。
- public: Java语言中访问限制最宽的修饰符,一般称之为“公共的”。被其修饰的类、属性以及方法不仅可以跨类访问,而且允许跨包(package)访问。
修饰符 | 同一个包 | 同一个类 | 子类[^1] | 整体 |
---|---|---|---|---|
private | √ | |||
default | √ | √ | ||
protected | √ | √ | √ | |
public | √ | √ | √ | √ |
子类,就是可以跨包【因为子类可能跨包,所以,为了使父类中属性、方法在子类能访问,父类最严格的访问控制只能是protected】
方法的重写和覆盖
在子类中可以根据需要对从父类中继承来的方法进行改造,即方法的重写和覆盖(注意:构造方法不能被继承),在程序执行时,子类的方法将覆盖父类的方法。
- 覆盖方法必须和被覆盖方法具有相同的方法名称、参数列表和返回值类型。【返回类型、方法名、参数列表必须完全相同,应用场景:继承,父类的成员方法只能被它的子类重写】。
- 覆盖方法不能使用比被覆盖方法更严格的访问权限。
构造方法的重载和重写
- 父类构造方法不能被子类继承,但是在实例化子类的时候会调用父类的构造方法,每个子类构造方法的第一条语句,都是隐含地调用super()
- 父类构造方法不能被子类重写
- 父类构造方法不能被子类重载
- 子类可以重载从父类继承过来的方法
- 子类可以重载自己默认的构造方法
- 子类可以重载父类的非构造方法
super关键字
super的功能
在Java类中使用super来引用父类的成分:
- super可用于访问父类中定义的属性
- super可用于调用父类中定义的成员方法
- super可用于在子类构造方法中调用父类的构造方法
- super的追溯不仅限于直接父类
- 子类重写的方法中,调用父类中被重写的方法,需要用super
调用父类构造方法
在子类的构造方法中可使用super(参数列表)语句调用父类的构造方法。
子类调用父类构造方法的两种方式
- 直接方式
- 子类构造方法中第一行直接super调用父类构造方法
- 间接方式
- 子类构造方法中第一行this调用重载的构造方法,被调用的那个重载构造方法中第一行super调用父类构造方法(只有构造方法才能调用一个类的构造方法)
子类调用父类构造方法的几种情况
- 父类有无参构造方法:
- 如果子类的构造方法中没有显示地调用父类构造方法,也没有使用this关键字调用重载的其它构造方法(调用子类中重载子类的构造方法),则系统默认调用父类无参数的构造方法【可以在这个无参构造方法体中写内容,但是不能有参数】;
- 如果子类构造方法中既未显式调用父类构造方法,而父类中又没有无参的构造方法,则编译出错。
- 父类只有有参构造方法:
- 方案一:子类的构造方法中第一行用super调用父类有参构造方法;
- 方案二:父类新增无参构造方法。
- 父类中同时存在有参和无参构造方法:
- 子类的构造方法的第一行可以调用父类的有参构造方法;
- 也可以不调用,这样就默认调用父类无参构造方法。
super(…)和this(…)使用注意事项:
- super(…)和this(…)调用语句不能同时在一个构造函数中出现;
- super(…)或this(…)调用语句只能作为构造函数中的第一句出现。
子类调用构造方法的过程
如果一个子类继承自一个父类,则其构造方法的调用次序为:父类构造方法,成员变量初始化,子类构造方法。父类的构造方法一定会在子类的构造方法中被调用,并且逐渐向上链接,直到使得每个父类使用的构造方法都能被调用到。
多态性及应用
多态性
在Java中,子类的对象可以替代父类的对象使用(父类类型的引用变量可以指向子类的对象)
- 一个变量只能有一种确定的数据类型
- 一个引用类型变量可能指向(引用)多种不同类型的对象
Person p = new Student();
Object o = new Person();//Object类型的变量o,指向Person类型的对象
o = new Student(); //Object类型的变量o,指向Student类型的对象
一个引用类型变量如果声明为父类的类型,但实际引用的是子类对象,那么该变量就不能再访问子类中新添加的属性和方法(子类有父类没有的属性和方法)
Student m = new Student();
m.school = “pku”; //合法,Student类有school成员变量
Person e = new Student();
e.school = “pku”; //非法,Person类没有school成员变量
属性是在编译时确定的,编译时e为Person类型,没有school成员变量,因而编译错误。
虚拟方法调用
- 正常的方法调用:
Person e = new Person();
e.getInfo();
Student e = new Student();
e.getInfo();
- 虚拟方法调用(多态情况下):
Person e = new Student();
e.getInfo(); //调用Student类中重写的getInfo()方法
编译时类型和运行时类型:编译时e为Person类型,而方法的调用是在运行时确定的,所以调用的是Student类的getInfo()方法。—— 动态绑定
在多态的情况下,调了父类的某个方法,但是这个方法被子类重写了,运行时,其实调的是子类重写的方法。【对象是子类的,所以调子类方法】
更多学习可查看Java虚拟机的方法调用内容,参考链接https://www.cnblogs.com/bigshark/p/11241834.html
instanceof 操作符
instanceof 操作符:用于判断该运算符前面引用类型变量指向的对象是否是后面类,或者其子类、接口实现类创建的对象。如果是则返回true,否则返回false。
- instanceof前后必须有继承关系,不管哪个继承哪个,否则类型不兼容报错
- 如果前面继承后面,结果为true
- 如果前面等于后面,结果为true
- 如果后面继承前面,结果为false
- 总之,有继承关系时,前面<=后面,就返回true
instanceof运算符用于强制类型转换之前检查对象的真实类型以避免类型转换异常,从而提高代码健壮性。
对象类型转换
基本数据类型转换
小的数据类型可以自动转换成大的数据类型:
long g = 20;
double d = 12.0f;
大的数据类型可以强制转换成小的数据类型:
float f = (float) 12.0;
int a = (int) 1200L;
Java对象的强制类型转换
对Java对象的强制类型转换称为造型。
- 从子类到父类的类型转换可以自动进行;
- 从父类到子类的类型转换必须通过造型(强制类型转换)实现;
- 无继承关系的引用类型间的转换是非法的,编译报错;
- 在造型前可以使用instanceof操作符测试一个对象的类型。
Object类及主要方法
Object类
Object类是所有Java类的根父类。如果在类的声明中未使用extends关键字指明其父类,则默认父类为Object类
Object类中的常用方法
equals()方法与操作符 ==
public class TestDeng {
public static void main(String[] args){
// 操作符 ==
// 1.基本数据类型:基本类型的值是否相等,符号两边的数据类型必须一致(可自动转换的基本数据类型除外),否则编译出错
// 2.引用数据类型:是否指向同一个对象,即:是否指向同一块內存空間,要求 == 两边的类型必须一致或存在着父子关系, 否则编译出错;
// equals()方法
// equals()方法只能比较引用类型,其作用与操作符 == 相同,比较是否指向同一个对象,比较的是内存地址
// 格式:obj1.equals(obj2)
// 注意:对于类File、String、Date及封装类来说,这些类中,已经重写了equals()方法,所以比较的是内容,不再比较是都指向同一个对象
// 所以比较两个字符串的內容是否相同, 一定要使用 equals() 方法, 而不能使用 ==
int i = 3;
int j = 3;
System.out.println(i == j); // true
char c = 3;
System.out.println(i == c); // true,int可自动转换为char类型
String s1 = "rose";
String s2 = "rose";
System.out.println(s1 == s2); //true
System.out.println(s1.equals(s2)); // true
String s3 = new String("name");
String s4 = new String("name");
System.out.println(s3 == s4); //false,s3和s4都是new出来的对象,分别指向两块内存,里面存放的内容一样,但是指向的内存地址不一样,所以s3==s4是false
System.out.println(s3.equals(s4)); // true,String类中,已经重写了equals()方法,比较的是内容,所以s3.equals(s4)是true
Student4 s5 = new Student4("jack",13);
Student4 s6 = new Student4("jack",13);
System.out.println(s5 == s6); // false,同理,两个引用变量指向的内存地址不一样
System.out.println(s5.equals(s6)); // false
}
}
toString() 方法
- Object 类定义的方法, 所以任何对象都可以来调用 toString() 方法
- 其返回值是String类型,返回值:全类名@hash码
- 可以根据需要在用户自定义类型中重写toString()方法,如String 类重写了toString()方法,返回字符串的值。
- 打印对象,会自动调对象的toString方法
public class TestToString {
public static void main(String[] args){
String s = "name";
System.out.println(s); // name,打印对象,会自动调对象的toString方法,相当于System.out.println(s.toString());
System.out.println(s.toString()); //name
Student4 s2 = new Student4("jack",23);
System.out.println(s2); // cn.java.demo.Student4@1b6d3586,返回全类名@hash码,相当于System.out.println(s2.toString());
System.out.println(s2.toString()); // cn.java.demo.Student4@1b6d3586
}
}
封装类
Java中的8种基本类型以及对应的封装类:
基本数据类型 | byte | short | int | long | float | double | char | boolean |
---|---|---|---|---|---|---|---|---|
封装类 | Byte | Short | Integer | Long | Float | Double | Character | Boolean |
自动装箱和自动拆箱
自动装箱时编译器调用valueOf将原始类型值转换成对象,同时自动拆箱时,编译器通过调用类似intValue(),doubleValue()这类的方法将对象转换成原始类型值。
学习来源1:https://blog.youkuaiyun.com/qq_34626097/article/details/83099355
学习来源2:https://www.cnblogs.com/uncleyong/p/9729141.html#_label5