一、面向对象的特性之二:继承
1、为什么使用继承
- ① 减少了代码的冗余,提高了代码的复用性
- ② 便于功能的扩展
- ③ 为之后多态性的使用,提供了前提有了,继承让类与类之间产生关系,我们可以创建更加特殊的类型(多态)
2、如何使用继承
关键字:extends ---- “扩展” 子类是父类的扩展
例如: class A extends B{}
子类:A 父类(超类、基类、SuperClass):B
3、通过继承,子类可以继承父类中所有的属性(包括私有的)和方法。
注意:父类中私有属性,也可以继承,只不过因为 private 修饰符的作用,子类不能直接访问
若要访问需要通过公共的 get/set 方法
子类继承父类以后,还可以声明自己特有的属性或方法:实现功能的拓展。
- 子类和父类的关系,不同于子集和集合的关系。
- extends:延展、扩展
4、继承的注意
①不能为了简化代码,获取某功能而继承,若要继承两个类之间要满足一定的所属关系:is a
②Java 只支持单继承,不支持多继承。(一个父类可以有多个子类,但是一个子类只能有一个父类)
③Java 支持多层继承
class A{
void test1(){}
void test2(){}
}
class B extends A{
//void test1(){}
//void test2(){}
}
class A{
void test1(){
//111111111111
}
}
class B{
void test1(){
//2222222222
}
}
class C extends A, B{}
C c = new C();
c.test1();
二、方法的重写(方法的覆盖 Override)
若父类中的方法对于子类来说不适用,子类可以对父类中的方法进行重写
前提:要有继承关系
①方法名与参数列表必须相同
②子类重写方法的访问控制修饰符不能小于父类被重写方法的访问控制修饰符
③子类重写方法的返回值类型是父类被重写方法返回值类型的子类
④子类重写方法抛出的异常类型范围不能大于父类被重写方法抛出异常类型的范围
(通常应用时,使方法签名一模一样)
【面试题】 Override 和 Overload 的区别?
方法重载(Overload)
前提:在同一个类中
①方法名必须相同
②参数列表必须不同(参数的个数、参数的类型)
注意:与返回值类型无关
重写:表现为多态性。
重载,是指允许存在多个同名方法,而这些方法的参数不同。编译器根据方法不同的参数表,对同名方法的名称做修饰。对于编译器而言,这些同名方法就成了不同的方法。它们的调用地址在编译期就绑定了。Java的重载是可以包括父类和子类的,即子类可以重载父类的同名不同参数的方法。
所以:对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法,这称为“早绑定”或“静态绑定”;
而对于多态,只等到方法调用的那一刻,解释运行器才会确定所要调用的具体方法,这称为“晚绑定”或“动态绑定”。
引用一句Bruce Eckel的话:“不要犯傻,如果它不是晚绑定,它就不是多态。”
三、super 关键字
super 使用方式与 this 几乎一模一样。
this : 使用在本类中,代表当前对象的引用
super:使用在子类中,代表父类对象的引用
super.属性
super.方法
我们可以在子类的方法或构造器中。通过使用"super.属性"或"super.方法"的方式,显式的调用父类中声明的属性或方法。但是,通常情况下,我们习惯省略"super."
特殊情况:当子类和父类中定义了同名的属性时,我们要想在子类中调用父类中声明的属性,则必须显式的使用"super.属性"的方式,表明调用的是父类中声明的属性。
特殊情况:当子类重写了父类中的方法以后,我们想在子类的方法中调用父类中被重写的方法时,则必须显式的使用"super.方法"的方式,表明调用的是父类中被重写的方法。
super(……) : 调用父类构造器
①当子类继承父类后,子类“所有”构造器中默认第一行第一句有一个隐式的 super()
super() 作用:调用父类无参构造器
super() 目的:子类继承父类后,继承了父类中所有的属性和方法,因此子类需要知道父类
是如何为对象进行初始化
②若父类中没有提供无参构造器,子类“所有”构造器必须显示调用父类中有参构造器
(保证在创建子类对象前先初始化父类)
③super() 和 this() 必须使用在当前构造器中可执行代码的首行
因此,super() 和 this() 二者不能同时出现
四、四种访问控制修饰符
private :私有的,可用于修饰属性、方法。 只能在本类中访问
default : 默认的(缺省的),可用于修饰属性、方法、类。可以在本类中、本包中。
(注意:default 并不是访问控制修饰符的关键字,在什么都不加的情况下就是 defualt)
protected : 受保护的,可用于修饰属性、方法。 在本类中、本包中、子类中。
public :公共的,可用于修饰属性、方法、类。在任何地方都可以访问
五、面向对象的特性之三:多态
一类事物的多种表现形态。 人 - 男人 女人
1、多态的体现
①方法的重载与重写
②对象的多态性
2、对象的多态性
父类的引用指向子类的对象。
Person p = new Man();//多态
p.eat();
p.sleep();
//p.smoking(); 编译不能通过
3、虚拟方法调用(动态绑定)
有了对象的多态性以后,我们在编译期,只能调用父类中声明的方法,
但在运行期,我们实际执行的是子类重写父类的方法。
在多态的情况下,编译时:“看左边”,看的父类的引用。(而父类中没有子类特有的方法)
运行时:“看右边”,看的是子类对象。(实际运行的是子类重写父类的方法)
.多态性使用的注意点:
对象的多态性,只适用于方法,不适用于属性(编译和运行都看左边)
7.关于向上转型与向下转型:
7.1 向上转型:多态
7.2 向下转型:
7.2.1 为什么使用向下转型:
有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致编译时,只能调用父类中声明的属性和方法。子类特有的属性和方法不能调用。如何才能调用子类特的属性和方法?使用向下转型。
7.2.2 如何实现向下转型:
使用强制类型转换符:()
7.2.3 使用时的注意点:
① 使用强转时,可能出现ClassCastException的异常。
② 为了避免在向下转型时出现ClassCastException的异常,我们在向下转型之前,先进行instanceof的判断,一旦返回true,就进行向下转型。如果返回false,不进行向下转型。
7.2.4 instanceof的使用:
① a instanceof A:判断对象a是否是类A的实例。如果是,返回true;如果不是,返回false。
② 如果 a instanceof A返回true,则 a instanceof B也返回true.其中,类B是类A的父类。
③ 要求a所属的类与类A必须是子类和父类的关系,否则编译错误。
7.2.5 图示:
- 面试题:
8.1 谈谈你对多态性的理解?
① 实现代码的通用性。
② Object类中定义的public boolean equals(Object obj){ }
JDBC:使用java程序操作(获取数据库连接、CRUD)数据库(MySQL、Oracle、DB2、SQL Server)
③ 抽象类、接口的使用肯定体现了多态性。(抽象类、接口不能实例化)
8.2 多态是编译时行为还是运行时行为?
运行时行为。
4、引用数据类型之间的转换
向上转型:子类转父类。系统自动完成
向下转型:父类转子类。需要使用强转符“(需要转换的类型)”
可能引发 ClassCastException 异常
Person p = new Man();//多态-向上转型
p.eat();
p.sleep();
Man man = (Man)p;//向下转型
man.smoking();
Woman woman = (Woman)p;//编译?YES 运行?NO!发生 CassCastException
Java 为上述CassCastException提供了相应的解决办法
5、instanceof 运算符
例如:
p instanceof Woman : 判断 p 引用指向的对象是不是 Woman 的本类类型及子类类型,若是返回 true
if(p instanceof Woman){
Woman woman = (Woman)p;
}
6、多态的应用
①多态数组:
Person[] persons = new Person[3];//数组中可以存储 Person 本类类型的对象及子类类型的对象
persons[0] = new Person();
persons[1] = new Man();
persons[2] = new Woman();
for(int i = 0; i < persons.length; i++){
persons[i].eat(); //虚拟方法调用
persons[i].sleep();
}
for(Person p : persons){//多态
p.eat(); //虚拟方法调用
p.sleep();
}
②多态参数:
//需求:展示一个男人吃饭和睡觉的功能
/*public void show(Man man){
man.eat();
man.sleep();
}
//需求:展示一个女人吃饭和睡觉的功能
public void show(Woman woman){
woman.eat();
woman.sleep();
}*/
public void show(Person p){//多态参数:可以接收Person本类类型的对象及子类类型的对象
p.eat();//虚拟方法调用
p.sleep();
}
六、对象的关联
简单的说,就是一个对象中使用了另一个对象
class Teacher{
private String name;
private int age;
private Computer computer;//对象的关联
public String say(){
return name + "," + age + "," + computer.getDetails();
}
}
class Computer{
public String getDetails(){
return "我是一台高配电脑"
}
}
七、java.lang.Object 类
是所有类的父类。若一个类没有显示的继承任何类时,默认 extends java.lang.Object。
java.lang 是默认包
1、既然 Obejct 类是所有类的父类,因此 Object 类中的内容是最具共性的。
2、既然 Obejct 类是所有类的父类,因此子类可以继承 Object 类中所有的方法。
3、既然 Obejct 类是所有类的父类,若 Object 类中的方法对于子类不适用,子类可以重写 Object 类中的方法
1、public boolean equals(Object obj)
①在 java.lang.Object 中
②作用是比较两个引用数据类型是否相等
③ Object 类中的 equals() 比较两个对象的地址值是否相等。(通过查看源码发现实际使用 == 完成的)
④若 Object 类中的 equals() 对于我们来说不适用,我们可以进行重写
class User{
String name;
int age;
//重写其equals()方法
public boolean equals(Object obj){
if(obj == this){
return true;
}
if(obj instanceof User){
User u = (User)obj;
return this.age == u.age && this.name.equals(u.name);
}
return false;
}
}
2、“==” 运算符
①基本数据类型:比较两个基本数据类型的值是否相等。相等返回 true
②引用数据类型:比较两个引用数据类型的地址值是否相等。相等返回 true
补充: == 符号使用时,必须保证符号左右两边的变量类型一致。
【面试题】== 与 equals 的区别?
3、public String toString()
返回当前对象的字符串变现形式
①在 java.lang.Object 类中
②若直接输出对象的引用时,默认调用 toString()
③Object 类中的 toString 返回的格式如下:
getClass().getName() + ‘@’ + Integer.toHexString(hashCode())
④若 Object 类中的 toString() 对于我们来说不适用,我们可以进行重写
class ObjectTest{
public static void main(String[] args){
Person p1 = new Person("张三", 18);
Person p2 = new Person("张三", 18);
String str1 = new String("张三");
String str2 = new String("张三");
System.out.println(p1.equals(p2));// false---true
System.out.println(str1 == str2);//false
System.out.println(str1.equals(str2));//true
System.out.println(p1.toString());
System.out.println(p1);
}
}
class Person /*extends java.lang.Object*/{
private String name;
private int age;
public Person(){}
public Person(String name, int age){
this.name = name;
this.age = age;
}
//重写 Object 类中的 equals
public boolean equals(Object obj){
if(this == obj){
return true;
}
if(obj instanceof Person){
Person p = (Person)obj;
if(this.name.equals(p.name) && this.age == p.age){
return true;
}
}
return false;
}
//重写 Object 类中的 toString()
public String toString(){
return "姓名:" + name + " 年龄:" + age;
}
}
八、static 修饰符
代表静态的,可用于修饰 属性、方法、代码块、**内部类
1、static 修饰的属性(静态变量或类变量)
①随着类的加载而加载,随着类的消失而消失(生命周期最长)
②static 修饰的属性被所有该类的对象所共享
③一旦某个对象修改该属性值,其他对象的该属性值也会随之改变
④静态变量的存在优先于对象
⑤可以通过“类名.类变量”的方式使用
属性,是否使用static修饰,又分为:静态属性 vs 非静态属性(实例变量)
-
实例变量:我们创建了类的多个对象,每个对象都独立的拥一套类中的非静态属性。当修改其中一个对象中的非静态属性时,不会导致其他对象中同样的属性值的修改。
-
静态变量:我们创建了类的多个对象,多个对象共享同一个静态变量。当通过某一个对象修改静态变量时,会导致其他对象调用此静态变量时,是修改过了的。
2.2 static修饰属性的其他说明:
-
① 静态变量随着类的加载而加载。可以通过"类.静态变量"的方式进行调用
-
② 静态变量的加载要早于对象的创建。
-
③ 由于类只会加载一次,则静态变量在内存中也只会存在一份:存在方法区的静态域中。
静态变量内存解析:
2、静态变量与实例变量
①内存中的位置不同
②生命周期不同
3、static 修饰的方法(静态方法或类方法)
①随着类的加载而加载
②静态方法的存在优先于对象
③可以通过“类名.类方法”的方式调用
④静态方法中不可以使用非静态成员,非静态方法中可以使用静态成员
⑤静态方法中不能使用 this 和 super
如何判定属性和方法应该使用static关键字:
关于属性
属性是可以被多个对象所共享的,不会随着对象的不同而不同的。
类中的常量也常常声明为static
关于方法
操作静态属性的方法,通常设置为static的
工具类中的方法,习惯上声明为static的。 比如:Math、Arrays、Collections
使用举例:
举例一:Arrays、Math、Collections等工具类
举例二:单例模式
举例三:
class Circle{
private double radius;
private int id;//自动赋值
public Circle(){
id = init++;
total++;
}
public Circle(double radius){
this();
// id = init++;
// total++;
this.radius = radius;
}
private static int total;//记录创建的圆的个数
private static int init = 1001;//static声明的属性被所对象所共享
public double findArea(){
return 3.14 * radius * radius;
}
public double getRadius() {
return radius;
}
public void setRadius(double radius) {
this.radius = radius;
}
public int getId() {
return id;
}
public static int getTotal() {
return total;
}
}
九、类的成员之一:代码块
属性、方法、构造器、代码块
1、非静态代码块
作用:可以在创建对象时,对对象的属性等进行初始化
①格式:类中的一对 {}
②每次创建对象时执行
③非静态代码块的执行优先于对象,内部可以输出语句,每创建一个对象,就执行一次非静态代码块
④用于为对象进行初始化(通常用于为构造器中的共性内容进行初始化)
⑤非静态代码块可以有多个,依次向下的顺序执行
2、静态代码块
作用:初始化类的信息
①格式:static{ }
②随着类的加载而加载,并且只加载执行一次
③静态代码块的执行优先于非静态代码块
④静态代码块中不能使用非静态成员,不能使用 this 和 super
⑤静态代码块可以有多个,依次向下的顺序执行。
属性的赋值顺序:
- ①默认初始化
- ②显式初始化/⑤在代码块中赋值
- ③构造器中初始化
- ④有了对象以后,可以通过"对象.属性"或"对象.方法"的方式,进行赋值
- 执行的先后顺序:① - ② / ⑤ - ③ - ④
十、final 修饰符
代表最终的,可用于修饰 变量、方法、类
①final 修饰的类不能被继承。比如:String类、System类、StringBuffer类
②final 修饰的方法不能被重写,比如:Object类中getClass();
③final 修饰的变量叫常量,一旦赋值,值不能改变
1)常量的命名规范:所有字母都大写,每个单词以"_"隔开。 如: XXX_YYY_ZZZ
2)**常量没有默认值,在使用前必须赋值(直接显示赋值、构造器赋值、代码块)
若选择构造器赋值,则保证每个构造器中都为常量赋值
static final 用来修饰属性:全局常量
十一 Java中的JUnit单元测试
步骤:
1.中当前工程 - 右键择:build path - add libraries - JUnit 4 - 下一步
2.创建Java类,进行单元测试。
此时的Java类要求:① 此类是public的 ②此类提供公共的无参的构造器
3.此类中声明单元测试方法。
此时的单元测试方法:方法的权限是public,没返回值,没形参
4.此单元测试方法上需要声明注解:@Test,并在单元测试类中导入:import org.junit.Test;
5.声明好单元测试方法以后,就可以在方法体内测试相关的代码。
6.写完代码以后,左键双击单元测试方法名,右键:run as - JUnit Test
说明:
1.如果执行结果没任何异常:绿条
2.如果执行结果出现异常:红条