继承
优势:
- 减少了代码的冗余,提高了代码的复用性
- 便于功能的扩展
- 为之后多态性的使用提供了前提
格式:
public class Student extends Person{}
- 一旦子类A继承父类B以后,子类A就获取了父类B中声明的所有属性和方法
- 特别的:父类中声明为private的属性和方法,子类继承父类以后,仍然认为获取了父类中私有的结构,但由于封装性的影响,使得子类不能直接调用父类的结构
规定:
- 只支持单继承和多层继承(一个父类可以有很多子类,一个子类只能有一个直接父类)
- 如果一个类没有显式声明它的父类,那么该类继承于java.lang.Object类,即所有的类都直接或间接的继承于Object类
方法的重写
子类继承父类以后,可以对父类中同名同参数的方法,进行覆盖操作。重写以后,创建子类对象以后,通过子类对象调用子父类中的同名同参数的方法时,实际执行的是子类重写父类的方法。
重写的规定:
方法的声明:权限修饰符 返回值类型 方法名(形参列表) throws 异常的类型 { 方法体 }
- 子类重写的方法的方法名和形参列表与父类被重写的方法的方法名和形参列表相同
- 子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符(特殊情况:子类不能重写父类中声明为private权限的方法)
- 返回值类型:
- 父类被重写的方法的返回值类型是void,则子类重写的方法的返回值类型只能是void
- 父类被重写的方法的返回值类型是A类型,则子类重写的方法的返回值类型可以是A类或A类的子类
- 父类被重写的方法的返回值类型是基本数据类型,则子类重写的方法的返回值类型必须是相同的基本数据类型
- 子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型
- 子类和父类中的同名同参数的方法,要么都声明为非static的(考虑重写),要么都声明为static的(不可以被重写)
super关键字
public class Person {
String name;
int age;
String id;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void eat(){
System.out.println("人吃饭");
}
public void sleep(){
System.out.println("人睡觉");
}
}
public class Student extends Person {
String major;
String id;
}
调用属性和方法:
- 在子类的方法或构造器中,通过使用
super.属性
或super.方法
的方式,显式的调用父类中声明的属性或方法,通常情况下习惯省略superpublic void info() { super.eat(); System.out.println(super.name + "学习"); }
- 当子类和父类中定义了同名的属性时,要想在子类中调用父类中声明的属性,则必须显式的使用
super.属性
的方式,表明调用的是父类中声明的属性public void info() { System.out.println("父类的id属性:" + super.id); System.out.println("子类的id属性:" + this.id); }
- 当子类重写了父类中的方法以后,要想在子类的方法中调用父类中被重写的方法时,则必须显式的使用
super.方法
的方式,表明调用的是父类中被重写的方法@Override public void sleep(){ System.out.println("学生需要睡8小时"); } public void info() { this.sleep(); super.sleep(); }
调用构造器:
在子类的构造器中显式的使用super(形参列表)
的方式,调用父类中声明的指定的构造器
public Student(String name, int age, String major) {
super(name, age);
this.major = major;
}
super(形参列表)
的使用,必须声明在子类构造器的首行- 在类的构造器中,针对于
this(形参列表)
或super(形参列表)
只能二选一,不能同时出现 - 在构造器的首行,没有显式的声明的
this(形参列表)
或super(形参列表)
,则默认调用的是父类中空参的构造器super()
- 在类的多个构造器中,至少有一个类的构造器中使用了
super(形参列表)
,调用父类中的构造器
子类对象实例化的全过程
- 从结果上来看:
子类继承父类以后,就获取了父类中声明的属性或方法。创建子类的对象,在堆空间中,就会加载所有父类中声明的属性 - 从过程上来看:
当通过子类的构造器创建子类对象时,一定会直接或间接的调用其父类的构造器,进而调用父类的父类的构造器,……直到调用了java.lang.Object类中空参的构造器为止。正因为加载过所有的父类的结构,所以才可以看到内存中有父类中的结构,子类对象才可以考虑进行调用。
明确:虽然创建子类对象时,调用了父类的构造器,但自始至终只创建过一个对象,即为new的子类对象
多态性
- 对象的多态性:父类的引用指向子类的对象(只适用于方法,不适用于属性)
- 虚拟方法调用:当调用子父类同名同参数的方法时,实际执行的是子类重写父类的方法
- 编译看左边,运行看右边:在编译期,只能调用父类中声明的方法,但在运行期,实际执行的是子类重写父类的方法
- 使用前提:1. 要有类的继承关系;2. 要有方法的重写
- 对于属性,编译和运行都看左边
instanceof 操作符
- 有了对象的多态性以后,内存中实际上是加载了子类也有的属性和方法的,但由于变量声明为父类类型,导致编译时只能调用父类中声明的属性和方法,子类中特有的属性和方法不能调用
- 为了调用子类特有的属性和方法,需要使用强制类型转换(向下转型)
Person p = new Man(); Man man = (Man) p;
- 使用强制类型转换时可能会出现ClassCastException异常,此时就需要使用instanceof来判断
// 此时存在ClassCastException异常 Person p = new Man(); Woman woman = (Woman) p; // 需要判断后再进行向下转型 if (p instanceof Man) { Man man = (Man) p; }
- 格式:
a instanceof A
判断对象a是否是类A的实例,如果是返回true,否则返回false - 如果
a instanceof A
返回true,且类B是类A的父类,则a instanceof B
也返回true - 对象a对应的类必须要与类A有子父类的关系
Object类
- 只有一个空参的构造器
finalize()
在垃圾回收之前调用(不要手动调用)clone()
复制一个对象
equals()
比较两个对象是否相等
==:运算符
- 基本数据类型:比较两个变量保存的数据是否相等,类型不一定相同
- 引用数据类型:比较两个对象的地址值是否相同,即两个引用是否指向同一个对象实体
- 在使用时,左右两边的变量数据类型要保持一致
equals():方法
- 只适用于引用数据类型
- Object类中的equals()与==的作用是相同的,即比较两个对象的地址值是否相同
- 像String,Date,File,包装类等都重写了Object类中的equals(),重写后比较的是两个对象的实体内容是否相同
toString()
- 当输出一个对象的引用时,实际上就是调用当前对象的toString()
- 像String,Date,File,包装类等都重写了Object类中的toString(),使得在调用对象的toString()时,返回实体内容信息
数组也作为Object类的子类出现,可以调用Object类中声明的方法
包装类的使用
Java提供了8种基本数据类型对应的包装类,使得基本数据类型的变量具有类的特征
- char对应的包装类是Character,int对应的包装类是Integer,其他的类都是首字母大写
- 数值型对应的包装类都是Number类的子类
基本类型,包装类与String类之间的相互转换
- 基本数据类型 --> 包装类:调用包装类的构造器
Integer in1 = new Integer(10); Integer in2 = new Integer("10"); // 其中Boolean类只要不是true,都认为是false Boolean b1 = new Boolean(true); Boolean b2 = new Boolean("true"); Boolean b3 = new Boolean("true123");// false
- 包装类 --> 基本数据类型:调用包装类的xxxValue()方法
Integer in1 = new Integer(10); int i1 = in1.intValue();
JDK5.0 以后可以进行自动装箱和自动拆箱
- 基本数据类型、包装类 —> String类型
int i1 = 12; // 1.连接运算 String s1 = i1 + ""; // 2.调用String重载的valueOf() String s2 = String.valueOf(i1);
- String类型 —> 基本数据类型、包装类
String string = "123"; // 调用包装类的parseXxx() int i = Integer.parseInt(string);
Integer内部定义了IntegerCache结构,IntegerCache中顶一个Integer[],保存了从-128 ~ 127范围内的整数,如果使用自动装箱的方式给Integer赋值的范围在-128 ~ 127之间时,可以直接使用数组中的元素,无需new新的对象
Integer m = 7;
Integer n = 7;
System.out.println(m == n);// true
Integer x = 128;
Integer y = 128;
System.out.println(x == y);// false