文章目录
Java——面向对象(中)
一、封装、继承、多态
面向对象核心思想【封装继承多态】
二、 虚方法
在Java中虚方法是指在编译阶段和类加载阶段都不能确定方法的调用入口地址,在运行阶段才能确定的方法,即可能被重写的方法。
- 先看这个对象的编译时类型,在这个对象的编译时类型中找到最匹配的方法, 最匹配的是指,实参的编译时类型与形参的类型最匹配。静态分派
- 再看这个对象的运行时类型,如果这个对象的运行时类重写了刚刚找到的那个最匹配的方法,那么执行重写的方法, 即动态绑定。否则仍然执行刚才编译时类型中的那个方法
- 注意: 成员变量和非虚方法不具有多态性。
案例一
public class Test08 {
public static void main(String[] args) {
A a1 = new A();
A a2 = new B();
B b = new B();
C c = new C();
D d = new D();
// b 因为在 a1.show里面没有对应的方法, 会找最匹配的 A
System.out.println("(1)" + a1.show(b)); // A and A
// a2 在编译时类型时 A, 会在 A中找对应的方法
System.out.println("(2)" + a2.show(d)); // A and D
System.out.println("(3)" + b.show(c)); // B and B
// B 继承A 就会有 A 中的方法 show(D obj)
System.out.println("(4)" + b.show(d)); // A and D
}
}
class A{
public String show(D obj){
return ("A and D");
}
public String show(A obj){
return "A and A";
}
}
class B extends A{
public String show(B obj){
return "B and B";
}
public String show(A obj){
return "B and A";
}
}
class C extends B{
}
class D extends B{
}
案例二
public class Test09 {
public static void main(String[] args) {
A a1 = new A();
A a2 = new B();
B b = new B();
C c = new C();
D d = new D();
// 找最匹配的类型
System.out.println("(1)" + a1.show(b)); // A and A
// 第一步看编译, a2 编译类型是 A, 然后在 A 中找最匹配的方法show(A obj)
// 第二步 运行使用的是 B 类, B 类重写了show(A obj)
System.out.println("(2)" + a2.show(d)); // B and A
// B 类继承 A, 就拥有了show(C obj) 方法
System.out.println("(3)" + b.show(c)); // A and C
System.out.println("(4)" + b.show(d)); // B and B
}
}
class A {
public String show(C obj) {
return ("A and C");
}
public String show(A obj) {
return "A and A";
}
}
class B extends A {
public String show(B obj) {
return "B and B";
}
public String show(A obj) {
return "B and A";
}
}
class C extends B {
}
class D extends B {
}
三、构造器和非静态代码块
3.1 构造器
我们发现我们
new
完对象时,所有成员变量都是默认值,如果我们需要赋别的值,需要挨个为它们再赋值,太麻烦了。Java 给我们提供了构造器(Constructor)
。直接为当前对象的某个或所有成员变量直接赋值呢。
- 构造器的作用
在
new
对象的时候为实例变量赋值。
- 构造器的语法
public class Person{
private String name;
private int age;
// 无参构造
public Person(){
}
// 有参构造
public Person(String name, String age){
this.name = name;
this.age = age;
}
}
- this调用本类其他构造器
public class Person{
private String name;
private int age;
private char sex;
// 有参构造
public Person(String name){
this.name = name;
}
public Person(String name, int age, char sex){
this(name);
this.age = age;
this.sex = sex
}
}
- super调用父类构造器
子类的每个构造器中默认第一行隐藏super()
, 即默认调用父类的无参构造器。
总之: 使用子类任意构造器创建对象时,必须要直接或间接通过super(【实参列表】)
先调用执行父类构造器。
3.2 非静态代码块
非静态代码块的作用和构造器一样,也是用于实例变量的初始化等操作,所以也称为构造代码块。
如果多个重载的构造器有公共代码,并且这些代码都是先于构造器其他代码执行的,那么可以将这部分代码抽取到非静态代码块中,减少冗余代码。
非静态代码块的执行特点
所有非静态代码块中代码都是在new对象时自动执行,并且一定是先于构造器的代码执行。
四、实例初始化过程
实例初始化过程:即
new
对象时, 需要调用构造器。代码底层会执行一个实例初始化方法 init{}, 此方法包含4
部分
(1)执行构造器中 super (【参数】); 调用父类构造器, 初始化父类数据
(2)执行直接显示赋值语句
(3)执行非静态代码块中语句
(4)执行构造器中其余代码
特别说明: 一定先执行 super(【参数】), 2, 3部分按照书写先后顺序执行。最后执行构造器中其余代码
五、this 和 super 关键字
- this:表示当前对象
- super:表示引用父类声明的成员
注意: super 不是表示父类对象, 而是类似于关键字表示查找范围从父类区域开始。
内存图
六、native 关键字
native
只能修饰方法,表示这个方法的方法体代码不是用Java语言实现的,而是由C/C++语言编写的。但是对于Java程序员来说,可以当做Java的方法一样去正常调用它,或者子类重写它。
内存图 native 修饰的方法在 内存中 的 本地方法栈中
运行,其他方法都是在虚拟机栈中执行。
七、final 关键字
final 表示最终的, 可以修饰
变量
,方法
,类
修饰类: 该类不能被继承
修饰方法: 该类不能被重写
修饰变量: 该变量是最终的, 不能改变
八、Object 类
java.lang.Object
是所有类的基类,类似于 祖宗类。每个类都使用Object
作为父类类。
如果一个类没有指定父类,那么默认则继承自Object类。
Object 类的常用方法
hashCode()
返回每个对象的
hash
码值。根据对象内存地址换算得到的一个整数。
主要用于后面当对象存储到哈希表等容器中时,为了提高存储和查询性能用的。
建议子类重写方法, 使其返回值与对象的属性相关。
hashCode 的常规协定:
- 如果两个对象的hash值是不同的,那么这两个对象一定不相等;
- 如果两个对象的hash值是相同的,那么这两个对象不一定相等。
getClass()
获取运行时类的 Class 对象。 因为 Java 有多态现象,所以一个引用数据类型的变量的编译时类型与运行时类型可能不一致,如果需要查看这个变量实际指向的对象的类型,需要用 getClass() 方法
通常用在反射
toString()
① 默认情况下,toString() 返回的是“对象的运行时类型名称 @ 对象的hashCode值的十六进制形式"
用来表示该对象的信息
② 通常是建议重写
③ 如果我们直接 System.out.println(对象),默认会自动调用这个对象的toString()
equals()
用于判断当前对象this与指定对象obj是否“相等”
① 默认情况下,equals方法的实现等价于与“==”,比较的是对象的地址值
② 我们一般选择重写
finalize()
finalize() 此方法由垃圾回收器调用, 通常用于释放一些c/c++占用的资源,子类可以覆盖该方法以实现资源清理工作,GC 在回收对象之前调用该方法。
也可以抽象的理解为 生命周期函数,可以在回收对象前做一些操作
九、JavaBean
遵循
“一定编程原则”
的Java类都可以被称作JavaBean,是一种Java语言编写的可重用组件,它的方法命名,构造及行为必须符合特定的约定
1、这个类必须具有一个公共的(public)无参构造函数;
2、所有属性私有化(private);
3、私有化的属性必须通过public类型的方法(getter和setter)暴露给其他程序,并且方法的命名也必须遵循一定的命名规范。
4、这个类应是可序列化的。(比如可以实现Serializable 接口,用于实现bean的持久性)