文章目录
面向对象的特征一:继承
继承的优势(why):
- 减少了代码的冗余,提高了代码的复用性
- 便于功能的扩展
- 为多态的使用,提供了前提
继承的格式(what)
class A extends B{}
A; 子类、派生类、subclass
B: 父类、超类、基类、superclass
- 子类A在继承父类B以后,子类A中就获取了B中所有的属性和方法。特别的,父类中声明private的属性或方法,子类继承父类后,仍然认为获取了父类中私有的结构。只是因为封装的影响,使得子类不能直接调用父类的结构。
- 子类继承父类以后,还可以声明自己特有的属性或方法,实现功能的拓展。
继承的规定
- 一个类可以被多个子类继承
- 一个类只能有一个父类(java不允许多继承)
- 子父类是相对的概念,可以实现多层继承。
- 子类直接继承的父类,成为直接父类,间接继承的父类成为间接父类。
- 子类继承父类以后,就获取了直接父类以及所有间接父类的
属性和方法.
Object 类
Object类的说明
- 如果我们没有显式的声明一个类的父类的话,则此类继承于java.lang.Object类
- 所有的java类(除java.lang.Object类之外)都直接或间接继承于java.lang.Object类
- 意味着,所有的java类具有java.lang.Object类声明的功能。
Object类的使用
- clone()方法:obj.clone()是深拷贝
- finalize()方法:garbage collection用于回收内存时使用,在对象回收之前会调用finalize() .最好不要主动使用。
- hashCode()
- notify()
- notifyAll()
- wait()
- equals()
- toString()
方法的重写(override)
- 子类继承父类后,可以对父类中同名同参数的方法,进行覆盖操作
- 应用:重写以后,当创建子类对象以后,通过子类对象调用父类中的同名参数的方法时,实际执行的是子类重写父类的方法。
- 方法重写中的规定
- 子类重写方法的格式:权限修饰符 返回值类型 方法名(形参列表){//方法体}
- 子类重写方法的方法名和形参列表与父类被重写的方法的方法名和形参列表相同
- 子类重写方法的
权限修饰符
不小于父类被重写的方法的权限修饰符
。(即父类中为public,子类中也必须为public,如果子类重写时权限为缺省的话就会报错) - 如果父类中的方法是private,那么子类中不会重写此方法,会依次运行。(代码证明)
- 返回值类型:
- void: 父类中被重写方法的返回值类型是void,则子类中重写的方法的返回值只能是void
- Object: 父类被重写方法的返回值类型是A类型,则子类中重写的方法的返回值可以是A类也可以是A类的子类
- 基本数据类型:父类被重写的方法如果是基本数据类型,则子类重写的方法的返回值类型必须是相同的数据类型
- 子类重写的方法抛出的异常类型不大于父类被重写方法抛出的异常
四种访问权限修饰符
相关代码验证:
父类:Qualifier
package qualifier;
public class Qualifier {
// 四种修饰符修饰属性
private int propertyPrivate = 1;
int propertyDefault = 2;
protected int propertyProtected = 3;
public int propertyPublic = 4;
// 四种修饰符修饰方法
//private
// 测试private声明的方法不能被重写
private void funcVoidPrivate(){
System.out.println("这里是 *private* void function");
}
// default
void funcVoidDefault(){
System.out.println("这里是 *default* void function!");
}
// protected
protected void funcVoidProtected(){
System.out.println("这里是 *protected* void function");
}
// public
public void funcVoidPublic(){
System.out.println("这里是 *public* void function");
this.funcVoidPrivate(); //测试子类不能重写私有方法
this.funcVoidProtected();
System.out.println("*public* void function结束!!!\n");
}
// 测试子类方法重写时,能否改变返回类型?
public int funcIntPublic(){
System.out.println("这里是 *public* int function");
return 1;
}
}
相同包下测试四种修饰符:
package qualifier;
// 测试同一包下四种修饰符的权限作用
// 同一包下类的private不可以访问
public class qualifierTest {
public static void main(String[] args) {
Qualifier qualifier = new Qualifier();
System.out.println(qualifier.propertyDefault);
System.out.println(qualifier.propertyProtected);
System.out.println(qualifier.propertyPublic);
// System.out.println(qualifier.propertyPrivate); 同一包下类的私有属性不可修改
qualifier.funcVoidDefault();
qualifier.funcVoidProtected();
qualifier.funcVoidProtected();
}
}
相同包下子类访问父类属性或方法
package qualifier;
//继承的子类中四种修饰符的权限
// 同一包下子类除了父类中private定义的属性和方法之外都可以调用
public class extendsTest{
public static void main(String[] args) {
Son SonClass = new Son();
System.out.println(SonClass.propertyDefault);
System.out.println(SonClass.propertyProtected);
System.out.println(SonClass.propertyPublic);
// System.out.println(SonClass.propertyPrivate); 同一包下类的私有属性不可修改
SonClass.funcVoidDefault();
SonClass.funcVoidProtected();
SonClass.funcVoidPublic();
System.out.println("*****");
SonClass.funcVoidPublic(); // 发现父类中的protected方法被重写了,但是父类中的private没有重写,仍然使用的父类的private声明的方法
}
}
class Son extends Qualifier{
//子类方法重写private声明的
public void funcVoidPrivate(){
System.out.println(("重写父类中私有方法"));
}
// 子类方法重写protected方法
public void funcVoidProtected(){
System.out.println("重写父类中protected方法");
}
}
不同包下访问类的属性或方法
package qualifier2;
// 不同包中没有继承关系
import qualifier.Qualifier;
// 测试不同包下四种修饰符的作用
public class qualifierTest {
public static void main(String[] args) {
Qualifier QualifierClass = new Qualifier();
System.out.println("888:" + QualifierClass.propertyPublic);
// System.out.println(QualifierClass.propertyDefault); 不同包下调用类只能使用public修饰的类
QualifierClass.funcVoidPublic();
}
}
不同包下子类访问父类的属性或方法
package qualifier2;
import qualifier.Qualifier;
// 测试不同包下子类的四种修饰符的作用
// 不同包下调用继承的子类 四种修饰符和重新new父类的效果是一样的只能访问pulibc的属性或方法
// 继承中的子类可以访问父类中的protected下的属性或方法
public class extendsTest {
public static void main(String[] args) {
Son SonClass = new Son();
// SonClass.propertyProtected = 1; //不能通过已经继承的子类中访问protected中的内容
SonClass.test();
System.out.println("public:" + SonClass.propertyPublic);
// System.out.println(SonClass.propertyPrivate); 同一包下类的私有属性不可见
}
}
class Son extends Qualifier {
public void test(){
System.out.println(this.propertyProtected);
this.funcVoidPublic(); //这里调用了父类的私有方法
}
}
super
super调用父类的属性或方法:
- 当子类方法重写了父类属性或方法后,如果想调用父类被重写的属性或方法的话可以用super来调用。
- 我们可以在子类的方法或构造器中。通过使用“super.属性"或"或"super.方法"的方式,显式的调用父类中声明的属性或方法。但是通常情况下,我们习惯省略"super"。
- 特殊情况:当子类和父类中定义了同名的属性时,我们要想在子类中调用父类中声明的属性,则必须显式的使用"super.属性"的方式,表明调用的时父类中声明的属性。
- 特殊情况:当子类重写了父类中的方法时,我们要想在子类中调用父类中被重写的方法,则必须显式的使用"super.方法"的方式,表明调用的是父类中被重写的方法。
super调用构造器
- 如果想使用父类中构造器初始化对象属性的结构,那么可以在子类的构造器中显式使用"super(形参)"的方法,调用父类声明的制定的构造器
- "super(形参列表)"的使用,必须声明在子类构造器的首行!
- 我们在类的构造器中,针对于”this(形参列表)"或"super(形参列表)"只能二选一,不能同时出现。
- 在子类构造器的首行,没有显式声明"this(形参列表)“或"super(形参列表)”,那么默认会有一个"super()"。
子类对象实例化的过程
- 从结果上看:(继承性)
子类继承父类后,就获取了父类中声明的属性或方法。创建子类的对象,在堆空间中,就会加载所有父类声明的属性。 - 从过程上看:
当我们通过子类的构造器创建子类对象时,我们一定会直接或间接的调用其父类的构造器。进而调用父类的构造器,知道调用了java.lang.Object类中空参的构造器为止。正因为加载过所有的父类的结构,所以才可以看到内存中有父类结构,子类对象才可以考虑进行调用。
明确:虽然创建子类对象时,调用了父类的构造器,但是自始至终就创建过一个对象,即new的子类对象。
面向对象特征之二:多态性
对象的多态性:父类的引用指向子类的对象
多态的使用:当调用子父类同名同参数的方法时,实际执行的是子类重写父类的方法–虚拟方法调用。
多态的使用:虚拟方法调用,有了对象的多态性以后,我们在编译时
,只能调用父类中声明的方法,但在运行时
,执行是是子类重写父类的方法。
- 多态使用的前提:1. 类的继承。2. 子类重写了方法
- 对象的多态性,只适用于方法,不适用于属性。(属性不能被重写,所以运行时还是运行的父类的)
- 多态的应用案例(节省了需要多个方法重载的代码)
虚函数(虚拟方法)
多态是运行时行为
- 子类中定义了与父类同参数的方法,在多态的情况下,将此时父类的方法称为虚拟方法,父类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译时是无法确定的。
- 编译时e为Person类型,而方法的调用是在运行时确定的,所以调用的是Student类的getInfo()方法。----动态绑定。
向下转型
- 多态调用父类时,是不能调用子类所特有方法或属性的,因为编译的时候变量是父类类型的。
- 有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致编译时只能调用父类中声明的属性和方法,子类特有的属性和方法不能调用。
- 那么如何才能调用子类特有的属性和方法?
这里可以使用强制类型转换符,也就是向下转型。
- 为了避免向下转型时类型错误,可以使用一个instanceof判断语句。
如果A instanceof 子类是true的话,那么A instanceof 父类也是true
多态:继承成员变量和继承方法的区别练习题
equals和==
==运算符
- == 符号必须保证两边变量类型一致
- 可以使用在基本数据类型变量和引用数据类型变量中。
- 在基本数据类型比较的是基本的数值,
- 在引用类型中比较的是引用的地址值。
equals()方法
- equals()方法只可以使用在引用数据类型中。
- Object中的equals()的定义:和==是一样的,都是比较对象的地址值。
- String,Date,File中重写了equals():比较的是字符串的内容
- 自定义重写equals(),
toString()
- 当我们输出一个对象的引用时,实际上就是调用当前对象的toString()
- Object类中toString()的定义:
return getClass().getName() + “@” + Integer.toHexString(hashCode()); - 像String、Date、File、包装类等都重写了Object类中的toString()方法。
单元测试方法
包装类
8个包装类
基本数据类型转包装类
Integer int1 = new Integer(10);
Integer int2 = new Integer("123");
Integer int3 = new Integer("123abc"); //报错
Boolean bool1 = new Boolean("TrUe") //true
Boolean bool2 = new Boolean("true123") //false
Boolea bool3; // bool3默认值为null,不是之前的false
包装类转基本数据类型
int i1 = in1.intValue(); // 包装类转基本数据类型
自动封箱拆箱
JDK5.0以上可以实现基本数据类型和包装类之间的自动风向和拆箱
double b1 = doub1; // 拆箱
Double doub2 = 2.0; // 装箱
基本数据类型,包装类和String之间的相互转换
- String转Double或者double,本身都可以,但是valueOf是先调用parseDouble然后重新new了一个Double返回,所以更适合返回Double类型。
Double d1 = Double.valueOf("12.2");
double d2 = Double.parseDouble("12.2");
- 数据类型转String
int num1;
Integer num2;
String str1 = num1 + "";
String str2 = String.valueOf(num2); // valueOf返回的是引用类型的包装类