面向对象编程(中)
继承
子类继承父类:例如Student类有可能继承Person类。
继承会使子类拥有父类的属性,方法。
public class Student extends Person{
}
继承性:
- 减少了代码冗余,增加了代码复用性(相同的属性和方法只需要在父类中定义)
- 便于功能的拓展(只需要修改父类中的信息)
子类继承父类以后就获取到了父类的所有结构,主要指:属性和方法。包括被封装的private 属性与方法,只是因为封装性的影响无法在子类中进行调用。
子类继承父类后还可以定义自己的功能,实现功能的拓展。
Java中一个类只允许有一个父类,而一个类可以被多个子类继承。子类直接继承的类叫做直接父类,间接继承的类(子类的父类的父类)叫做间接父类。
如果一个类没有显式的继承某个类,则其必定继承与java.lang.Object类,Object类是所有类的父类,其提供了很多方法。
重写
子类可以通过重写来实现对父类继承来方法的覆盖,即用新的方法替代父类中的方法。
重写的方法应该与父类中的方法名和形参列表都一致。
子类重写方法的权限应不小于父类中被重写的方法
特例:当父类中被重写的方法为private时,子类就不可见该方法,重写就相当于定义了一个自己的新方法,无所谓重写与否。
返回值类型:
- 若为void,则重写方法也只能是void
- 若为基本数据类型(double),重写方法也只能是相同数据类型(double)。
- 若为引用数据类型(Object),重写方法可以是其本身或其子类(Object、String)
static的方法不能够重写,其随着类的加载而加载。.
protected
protected 的范围介于缺省和public之间,若在不同的包中,一个包中类是另一个包中的子类,父类中定义了protected的属性或方法,则该属性和方法可以在子类中被调用。缺省则不能在这种情况下被可见。
若在不同包下,还没有继承关系,则只有public可见。
super
super理解为父类的xxx
super可以调用属性、方法、构造器
若子类与父类有方法的重写,或有相同属性时,this.xxx
指的是子类中的属性或方法,super.xxx指的是父类中的属性或方法。
super可以用来调用父类的构造器,如同this可以调用本类中的构造器,但和this一样的是,super调用构造器也必须写在首行,正因如此,每个构造器只能调用一种构造器,要么是本类中的其他构造器(this()),要么是父类中的某一个构造器(super())
若构造器中首行没有显示的调用某个构造器,则其实是默认调用一次父类的空参构造器。
子类对象实例化的过程
子类在实例化之后,或在堆空间中加载子类的属性与方法以及从父类中继承的属性和方法,直到加载完Object。
代码示例
package com.bainan.java2;
/**
* @ClassName Person
* @Description TODO
* @Author @Bainan
* @Date 2022/8/16 0:06
* @Version 1.0
*/
public class Person {
String name;
int age;
int id = 1001;
public Person(){}
public Person(String name){
this.name = name;
}
public Person(String name, int age){
this(name);
this.age = age;
}
public void eat(){
System.out.println("吃");
}
}
package com.bainan.java2;
/**
* @ClassName Student
* @Description TODO
* @Author Bainan
* @Date 2022/8/16 0:06
* @Version 1.0
*/
public class Student extends Person{
String major;
int id = 1102;
public Student(){}
public Student(String major){
this.major = major;
}
public Student(String name, int age, String major){
super(name, age);
this.major = major;
}
public void eat(){
System.out.println("学生多吃");
}
public void study(){
System.out.println("学生学习");
}
public void show(){
System.out.println(this.id + " " + super.id);
}
public void show1(){
System.out.println("name = " + name + age + major);
}
}
package com.bainan.java2;
/**
* @ClassName PersonTest
* @Description TODO
* @Author @Bainan
* @Date 2022/8/16 0:11
* @Version 1.0
*/
public class PersonTest {
public static void main(String[] args) {
Student stu = new Student();
stu.show();
Student stu1 = new Student("李白", 16, "英语");
stu1.show1();
}
}
多态
一个事务的多种形态
父类的引用指向子类的具体对象,声明父类,new子类
Person p1 = new Man();
实际调用的是子类重写过的方法:虚拟方法调用。
多态性创建的对象,只能调用子类重写过的方法,不能调用自身新创建的方法,编译器看的是对象的声明,即父类中有的方法,运行时看的是具体的对象,即子类。
多态性必须有重写@Override
多态性只适用于方法,不适用于属性
p1.id; //调用的是父类的id,不会调用子类的id
此时编译和运行都看声明的对象,而不看右边创建的对象。
多态性是运行时行为,有时不确定创建的对象多态后具体是那个子类对象,只有在真正运行时才确定。
调用地址的绑定在编译期就确定:重载
调用的地址在运行期才绑定:重写
向下转型
例如Man类继承Person类
Person p1 = new Man();
//若希望调用Man中特别的方法。
//Man m1 = p1; //错误写法。
Man m1 = (Man)p1; //通过强制类型转换的方式进行赋值。
向上转型指多态
instanceof
a instanceof A;
用来判断a是否为A实例化的对象,若是则返回true,不是则会返回false。
判断堆空间中new的具体对象是什么类型,即new后的类型。
a instanceof A
中,若A有父类B,则a instanceof B
也会返回true。
具体的理解:
向上转型,即多态性:Object obj = new Woman();
其中obj可以调用Object中的方法和属性以及在Woman中重写过的方法。
向下转型,即强制类型转换Person p = (Person)obj;
令p可以调用p中的属性与其独有的方法,
Object类的使用
Object 没有自己声明属性,只有一个空参构造器。
==与isequals()
==
在基本数据类型中,判断的是其中的数据是否相等(boolean 不能判)
在引用数据类型中,会判断地址是否相同,即两个引用是否指向同一个对象实体。
isequals()
isequals()只能用在对象(引用数据类型)的判断中,不能用于基本数据类型,因为其不是对象,不能调方法。
注:形参位父类对象(Object等),实际传值位为子类,是为多态性的应用。
Object类中定义的isequals()方法就是使用了“”符号。
String、Data等类中都继承并重写了Object中的isequals()方法,他们的具体方法就不再是“”了。
isequals()可以自动生成
toString()
当我们输出一个对象的引用时,就相当于调用了该对象的toString()方法
/*
两个完全相同
*/
System.out.println(cust);
Systrm.out.println(cust.toString());
toString()在Object类中的源码:
/*
实际上只是输出通过hashCode计算出的虚拟地址值.
前面还有自定义的包和类名。
*/
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
像String、Data、File、包装类等类都重写了toString()方法。
使得在调用toString()时会返回其实体内容。
单元测试方法
单元测试类的创建要加@Test注解,并import org.junit.Test;
单元测试要求类的权限必须是public的,必须有无参的构造方法,单元测试方法必须是void型的,且没有形参。
注:非静态的方法可以不创建对象。
单元测试方法可以简单的测试方法。
包装类
由于基本数据类型不能定义其方法,int i;
后,如果想定义关于i的一些方法比如转换成字符串,就不能实现。i.();//是不能实现的写法
故希望基本数据类型也可以定义一些丰富的方法,包装类应运而生。
//其中整型与浮点型都继承于父类Number
byte -> Byte
short -> Short
int -> Integer
long -> Long
float -> Float
double -> Double
boolean -> Boolean
char -> Character
基本数据类型不可以调用方法,所以需要包装类
基本数据类型->包装类,调用包装类的构造器;
int i = 10;
Integer i1 = new Integer(10); //创建对象,调用构造器
Integer i2 = new Integer("56"); //重载的另一个构造器
类不可以进行加减乘除运算,所以包装类转换成基本数据类型是必要的
包装类->基本数据类型
调用包装类的.xxxValue();
方法
Integer i1 = new Integer(5);
int i = i1.intValue();
System.out.println(i + 1);
JDK5.0提供了包装类的更简便使用方法:自动装箱与自动拆箱:
即可以不通过创建对象的方式将基本数据类型转换成包装类。
即对于包装类来说,基本数据类型可以直接赋值给对应包装类类型的类,这叫做自动装箱。
同样的,类也可以直接赋值给对应的基本数据类型,这叫做自动拆箱
有自动装箱与自动拆箱以后,就不再需要上面的两种方法了。
int num1 = 56;
Integer nu = num1; //自动装箱
Integer n2 = 78;
int num2 = n2; //自动拆箱
基本数据类型、包装类转换为String
调用String.valueOf();
方法
Double d1= new Double(12.3);
String str = String.valueOf(d1); //valueOf()重载了很多方法,可以把所有的基本数据类型转换为String,若传入包装类则会自动拆箱。
String str2 = String.valueOf(d2); //传入的是基本数据类型
JDK5.0以后,使用自动装箱与自动拆箱之后的String类型转换为基本数据类型或包装类:调用xxx.parseXxx(String);
方法
String str1 = "123";
int i = Integer.parseInt(str1);