面向对象编程(下半)
继承
面向对象的三大特征: 封装,继承,多态
继承的作用?能解决什么样的实际问题?
生活中的继承:将一方所拥有的东西给予另一方。父母的财富
类之间的继承:属性和方法的赠与或获得。
继承的作用:把多个子类中重复的代码抽取到父类中,提高代码的复用性。
子类可以在父类的基础上,增加其它功能,使子类更强大。
创建一个父类
父类Animal
package cn.it.Demo07.ch2.Demo02;public class Animal {String breed;int age;String sex;void eat(){System.out.println(breed+"吃饭中ing...");}void sleep(){System.out.println(breed+"睡觉中ing...");}
}
创建一个子类
语法:class 子类 extends 父类
子类1:鸟类
package cn.it.Demo07.ch2.Demo02;public class Bird extends Animal{String color;void fly(){System.out.println(breed+"飞翔中ing...");}
}
子类2:狗类
package cn.it.Demo07.ch2.Demo02;public class Dog extends Animal{String color;void run(){System.out.println(breed+"运动中ing...");}
}
子类3:鱼类
package cn.it.Demo07.ch2.Demo02;public class Fish extends Animal{void swim(){System.out.println(breed+"游泳中ing...");}
}
子类4:蛇类
package cn.it.Demo07.ch2.Demo02;public class Snake extends Animal{void crawl(){System.out.println(breed+"爬行中ing...");}
}
测试类Demo01
package cn.it.Demo07.ch2.Demo02;public class Demo01 {public static void main(String[] args) {Dog dog = new Dog();dog.breed="金毛";dog.age = 2;dog.sex = "公";dog.run();dog.eat();dog.sleep();Bird bird = new Bird();bird.breed="金丝雀";bird.age = 3;bird.sex = "母";bird.fly();bird.eat();bird.sleep();Snake snake = new Snake();snake.breed="蝰蛇";snake.age = 1;snake.sex = "母";snake.crawl();snake.eat();snake.sleep();Fish fish = new Fish();fish.breed="珍珠鱼";fish.age = 1;fish.sex = "母";fish.swim();fish.eat();fish.sleep();}
}
类的继承特点
-
在Java中,类只支持单继承,不允许多重继承。也就是说一个类只能有一个直接父类。
class A{}
class B{}
class C extends A,B{}
C类不可以同时继承A类和B类。
-
多个类可以继承一个父类。
class A{}
class B extends A{}
class C extends A{}
类B和类C都可以继承类A
-
在Java中,可以多层继承,即一个类的父类可以再继承另外的父类。C类继承自B类,而B类又可以继承自A类,这时,C类也可称作A类的子类。
class A{}
class B extends A{} // 类B继承类A,类B是类A的子类。
class C extends B{} // 类C继承类B,类C是类B的子类,同时也是类A的子类。
-
在Java中,子类和父类是一种相对概念,一个类可以是某个类的父类,也可以是另一个类的子类。例如,B类是A类的子类,同时又是C类的父类。
class A{}
class B extends A{} // 类B继承类A,类B是类A的子类。
class C extends B{} // 类C继承类B,类C是类B的子类,同时也是类A的子类。
4种访问控制权限
-
private(当前类访问级别):private属于私有访问权限,用于修饰类的属性和方法。类的成员一旦使用了private关键字修饰,则该成员只能在本类中进行访问。
-
default:如果一个类中的属性或方法没有任何的访问权限声明,则该属性或方法就是默认的访问权限,默认的访问权限可以被本包中的其它类访问,但是不能被其他包的类访问。
-
default:如果一个类中的属性或方法没有任何的访问权限声明,则该属性或方法就是默认的访问权限,默认的访问权限可以被本包中的其它类访问,但是不能被其他包的类访问。
-
public:public属于公共访问权限。如果一个类中的成员使用了public访问权限,则该成员可以在所有类中被访问,不管是否在同一包中。
public class Test {public int aa; // 可以被所有的类访问protected boolean bb; // 可以被所有子类以及本包的类使用private String cc; // 能在本包范围内使用void dd(){ // 可以在本类中访问System.out.println("包访问权限");}
}
不可继承
- 构造方法
类中的构造方法,只负责创建本类对象,不可继承。
- 私有属性和方法(private)
在继承中,子类不能继承父类中的私有成员。
- 默认属性和方法(default)
父类和子类不在同一个包中,不可以继承 default修饰的属性和方法。
父类和子类在同一个包中,可以继承 default修饰的属性和方法。
父类方法的重写
第一:子类中是否可以定义和父类相同的方法?
第二:为什么要重写?
第三:当父类提供的方法无法满足子类需求时,可在子类中定义相同的方法进行覆盖。
package cn.it.xheShi;public class Test {static class Animal {void eat() {System.out.println("父类未修改");}}static class Dog extends Animal {void eat() {System.out.println("子类已修改");}}public static void main(String[] args) {Dog dog = new Dog();dog.eat();}
}
方法重写要求
- 方法名称、参数列表、返回值类型必须与父类相同。
- 访问修饰符不能比父类更严格。
- 子类覆盖父类方法后,调用时优先执行子类覆盖后的方法。
super关键字
package cn.it.xheShi;public class Test {static class Animal {void eat() {System.out.println("父类未修改");}}static class Dog extends Animal {void eat() {super.eat();System.out.println("子类已修改");}}public static void main(String[] args) {Dog dog = new Dog();dog.eat();}
}
在子类中加上super关键字会保留父类的内容
super调用父类的成员变量
不同位置的变量i
package cn.it.xheShi;class Animal {int i = 10;
}class Dog extends Animal{String color;int i = 20;public void eat(){int i = 30;System.out.println(i);System.out.println(this.i);System.out.println(super.i);}
}
测试类
package cn.it.xheShi;public class Test {public static void main(String[] args) {Dog dog = new Dog();dog.eat();
// 30
// 20
// 10}
}
super与this关键字的作用非常相似,都可以调用构造方法、普通方法和属性,但是两者之间还是有区别的,super与this的区别如下表。
final关键字
final的英文意思是“最终,不可更改”。在Java中,可以使用final关键字声明类、属性、方法,在声明时需要注意以下几点:
-
使用final修饰的类不能有子类。
-
使用final修饰的方法不能被子类重写。
-
使用final修饰的变量(成员变量和局部变量)是常量,常量不可修改。
- Java中的类被final关键字修饰后,该类将不可以被继承。
package cn.it.xheShi;final class Animal {}class Dog extends Animal{
// Animal处报红
}
- 当一个类的方法被final关键字修饰后,这个类的子类将不能重写该方法。
package cn.it.xheShi;class Animal {public final void shout(){}
}class Dog extends Animal{public void shout(){//方法shout无法被重写System.out.println("Dog");}
}
- Java中被final修饰的变量是为常量,常量只能在声明时被赋值一次,在后面的程序中,其值不能被改变。
package cn.it.xheShi;class Animal {public static void main(String[] args) {final int AGE = 18; //变成常量了AGE = 20; //变成常量赋值后无法再修改}
}
抽象类abstract
为什么要有抽象类
假设:你创建了一个计时器,有别人想用你的计时器但发现没办法完全满足他所需要的功能,而每个想用这个东西的人需求各不同,他们就需要把名字继承下来再去修改内容
如何限制这种对象的创建?
使用abstract修饰类,此类不能new对象,称为抽象类。
抽象方法
抽象方法是使用abstrac关键字修饰的成员方法,抽象方法在定义时不需要实现方法体。
当一个类包含了抽象方法,该类必须是抽象类。抽象类和抽象方法一样,必须使用abstract关键字进行修饰。
abstract 返回值类型 方法名称(参数);
package cn.it.Demo07.ch2.Demo03;
//抽象类的作用
//当父类的方法无法完全满足所有子类所需的方法,通过抽象化在父类不定义内容(且无法被new出来),由子类来写,再使用
//继承抽象类必须实现里面的所有抽象方法
public abstract class Animal {//返回值类型 方法名//抽象后无法写方法体也就是{}的内容abstract void shout();abstract void eat();
}
class Dog extends Animal{@Overridevoid eat() {System.out.println("吃饭");}@Overridevoid shout() {System.out.println("你在狗叫什么!");}
}class TestAnimal{public static void main(String[] args) {Dog dog = new Dog();dog.shout();dog.eat();}
}
抽象类总结
抽象类的定义规则如下:
- 包含抽象方法的类必须是抽象类,抽象类不能实例化。
- 抽象类和抽象方法都要使用abstract关键字声明。
- 如果一个类继承了抽象类,那么该子类必须实现抽象类中的全部抽象方法。
- 抽象类中不一定有抽象方法,但有抽象方法的类一定是抽象类。
接口
接口的作用:克服单继承的限制,因为一个类只能有一个父类
如果一个抽象类的所有方法都是抽象的,则可以将这个类定义接口。接口是Java中最重要的概念之一,接口是一种特殊的类,由全局常量和公共的抽象方法组成,不能包含普通方法。
JDK 8对接口进行了重新定义,接口中除了抽象方法外,还可以定义默认方法和静态方法,默认方法使用default关键字修饰,静态方法使用static关键字修饰,且这两种方法都允许有方法体。
接口的语法格式:
[public] interface 接口名 [extends 接口1,接口2…] {
**[public] [static] [final] 数据类型 常量名 = 常量;****[public] [abstract] 返回值的数据类型 方法名(参数列表);**
[public] static 返回值的数据类型 方法名(参数列表){}
**[public] default 返回值的数据类型 方法名(参数列表){}**
}
接口
//InterfaceA.java
public interface InterfaceA{public void start();//抽象方法声明public void stop();//抽象方法声明}//实现类
//MovingDisk.java
public class MovingDisk implements InterfaceA{//方法实现public void start(){System.out.println("开始");}public void stop(){System.out.println("结束");}
}//测试类
//Test.java
public class Test{public static void main(String[] args){InterfaceA a1=new MovingDisk();//将移动硬盘插入到接口Aa1.start();//开启移动硬盘a1.stop();//关闭移动硬盘}
}
一个类可以实现多个接口,但只能继承一个父类(单继承)。
实现多个接口
public class 类名 implements 接口1,接口2,接口3{//接口中的方法实现
}
当实现多个接口时,注意必须实现每一个接口所声明的方法。接口作为类型可以声明一个对象的引用变量,实现接口的类实例化后,其对象的引用可以保存在这个变量中,在通过引用变量访问方法。实际将调用哪个方法,将根据实际创建出来的对象调用相对应的实现方法(此处表现出程序通过接口找到方法的多态性)。在实现对象时,有必要时需要做接口转换,
假设b对象都实现了接口1和接口2,则可以将b对象进行接口转换与实现,将其转换后的接口与实现赋予另一个对象
//a1接收b对象转换后的接口1与实现
接口1 a1=(接口1) b;
a1.接口1的方法;
//a2接收b对象转换后的接口2与方法实现
接口2 a2=(接口2)b;
a2.接口2的方法;
子类同时继承抽象类和实现接口,代码举例
//Test.java
//接口A
interface A{public String name="nacy";//定义全局变量public void print();//定义抽象方法
}
//抽象类B
abstract class B{public abstract void say();
}
//子类同时实现接口,覆写抽象类B的抽象方法
class Child extends B implements A{//覆写抽象类B中的抽象方法public void say(){System.out.println("Hello");}//覆写接口A中的抽象方法public void print(){Systeem.out.println("Name:"+name);}
}
public class Test{public static void main(String [] args){Child c=new Child();//实例化子类对象c.say();//调用被覆盖过的方法c.print();//调用被覆写过的方法}
}
抽象类实现接口:
interface A{public String name="nacy";//定义全局变量public void print();//定义抽象方法
}//抽象类B,实现接口
abstract class B implements A{public abstract void say();//此时抽象类中存在两个抽象方法:print()和say()
}//子类继承抽象类B
class Child extends B {//覆写抽象类B中的抽象方法public void say(){System.out.println("Hello");}//覆写抽象类B中的抽象方法public void print(){Systeem.out.println("Name:"+name);}
}
public class Test{public static void main(String [] args){Child c=new Child();//实例化子类对象c.say();//调用被覆盖过的方法c.print();//调用被覆写过的方法}
}
在Java中,允许一个接口继承多个接口,允许抽象类继承接口,但不允许接口继承抽象类。注意,不要混淆子类继承接口(implements)、子类继承父类(extends)、子接口继承父接口(extends)的关键字。
单个接口可以同时继承多个父接口:
//接口继承多个父接口
public interface 子接口名 extends 接口1,接口2,接口3{//接口中的方法实现
}
接口的多继承:
interface A{public String name="A";public void printA();
}
interface B{public void printB();
}//定义接口C,同时继承接口A和B
interface C extends A,B{public void printC();
}//子类实现接口C,并覆写所有接口的抽象方法
class Child implements C{public void printA(){System.out.println("A");}public void printB(){System.out.println("B");}public void printC(){System.out.println("C");}
}
public class Test{public static void main(String[] args){Child c=new Child();//实例化子类对象c.printA();//调用方法c.printB();c.printC();}
}