day10
- final关键字
final : 关键字,表示最后的,最终的,不可改变的
final: 修饰符 - final 可以修饰类 : 不能被继承,不能当父类,但是类本身可以正常的创建对象使用
代码
// 定义一个使用final修饰的类: 不能当父类,不能被继承
public final class FinalClass {
public static void main(String[] args) {
System.out.println(“Hello World!”);
}
}
// final修饰的类的子类,报错了,FinalClass是final修饰的
// final修饰的类不能被继承
public class FinalClassSub extends FinalClass{
}
- final 可以修饰变量 : final修饰的变量,只能手动的赋值一次,再也不能修改了,通常情况下,都是使用final修饰成员变量
举例 : π参数,值固定 3.1415926…
π实际定义在Math类中,是一个使用final修饰的变量 final double π = 3.14…
说明 : final修饰的变量,称为常量
final修饰的成员变量的赋值时机:
- 定义就直接赋值,最推荐
- 构造方法中赋值
代码
// 验证final修饰的变量和方法的特点
public class FinalDemo {
int i = 10 ;
// 1) 定义就直接赋值 ,最推荐的赋值方式
final int j = 99 ;
// final修饰的成员变量,必须在创建对象完成之前,进行赋值
// new 完成之前
final int z ;
//final修饰的成员变量在构造方法中赋值
public FinalDemo(){
z = 100;
}
public static void main(String[] args) {
FinalDemo fd = new FinalDemo();
fd.i = 88 ;
// final修饰的变量,数值不能修改,final修饰的变量,只能有一次手动赋值
// fd.j = 999 ;
System.out.println(fd.i);
System.out.println(fd.j);
System.out.println(fd.z);
}
}
- final 可以修饰方法 : 这个方法不能被重写(子父类继承关系中,子类重写父类中的方法)
代码
// 验证final修饰的变量和方法的特点
public class FinalDemo {
int i = 10 ;
// 定义就直接赋值 ,最推荐的赋值方式
final int j = 99 ;
final int z ;
public FinalDemo(){
z = 100;
}
// 定义一个使用final修饰的方法
public final void finalMethod(){
System.out.println("我是一个final修饰的方法");
}
}
public class FinalDemoSub extends FinalDemo{
// 从父类FinalDemo中继承到了 i , j ,z 变量
// 继承到了finalMethod 方法
/*
错误代码,父类中使用final修饰的finalMethod方法
不能被子类重写
@Override
public final void finalMethod(){
System.out.println("我是一个final修饰的方法的子类重写");
}*/
public static void main(String[] args) {
FinalDemoSub f = new FinalDemoSub();
// 子类继承使用父类中final成员
System.out.println(f.i);
System.out.println(f.j);
System.out.println(f.z);
f.finalMethod();
}
}
- 多态
多态是面向对象一大特征
2.1 多态的概念
多态 : 表示事物的多种形态
举例 : 人类,Person类
人类的表现方式,工程师,医生,学生,…
Animal 动物,cat,dog,鳄鱼,蛇…
多态的定义:
- 具有子父类的继承关系
- 需要子类重写父类方法
- 父类的引用指向子类的对象
代码
// 定义一个父类Person
public class Person {
public void work(){
System.out.println(“人类需要工作”);
}
}
// 定义一个工程师类,继承人类,因为工程师也是人类的一种
public class Employee extends Person{
@Override
public void work(){
System.out.println(“工程师开发代码”);
}
}
// 医生类继承Person类,因为医生也是人类的一种表现形式
public class Doctor extends Person{
public void work(){
System.out.println(“医生治病求人”);
}
}
public class TestPerson{
public static void main(String[] args) {
// 多态 : 父类的引用指向子类的对象
// Person p----> 父类的引用
// new Employee ----> 子类对象
Person p = new Employee();
p.work();// 工程师开发代码
// 创建一个医生对象
Doctor d = new Doctor();
//多态
Person p1 = new Doctor();
p1.work(); // 医生治病求人
}
}
2.2 多态中成员变量的访问特点
编译看等号左边,运行等号左边的变量(成员变量严格意义上,不玩多态)
编译 : 执行javac命令的时候,是否报错,写代码时
运行 : 执行java命令的时候
- 编译看左边 : 表示如果父类中没有定义这个变量,那么在类中使用这个变量,写出来就会报错
- 运行看左边: 变量的运行结果,与等号左边的类中定义的变量中保持一致
代码
// 定义一个父类
public class Fu{
int a = 10;
int b = 15;
}
// 定义子类,继承父类
public class Zi extends Fu {
// 1. 子类从父类中继承到了变量 a , b
int c = 99 ;
// 2. 定义一个与父类相同的变量
int a = 999;
}
// 测试多态的访问特征
public class TestFu{
public static void main(String[] args) {
// 1. 验证多态成员变量在多态中访问特点
Fu f = new Zi();
// 多态中,成员变量编译和运行看等号左边的类
System.out.println(f.a);// 10
System.out.println(f.b);// 15
Zi z = new Zi();
System.out.println(z.a);// 999
System.out.println(z.b);// 15
// 子类特有的成员变量c,因为Fu类中,没有定义c变量
// System.out.println(f.c); 不能运行。
}
}
2.3多态中方法的访问特点
编译看左边,运行看右边(子类重写)----> 这是多态的表体现方式
- 编译看左边 : 如果父类中,没有定义这个方法,那么无法通过多态调用到这个方法,因此编译错误
- 运行看右边 : 方法运行时,运行的子类中的重写方法
代码
// 定义一个父类
public class Fu{
public void eat(){
}
}
// 定义子类,继承父类
public class Zi extends Fu {
// 1. 重写父类中的eat方法
@Override
public void eat(){
System.out.println(“一会去吃饭”);
}
// 2.子类定义一个特有的功能
public void sleep(){
System.out.println("吃完饭,休息一会");
}
}
// 测试多台的访问特征
public class TestFu{
public static void main(String[] args) {
Fu f = new Zi();
// 验证多态中方法的访问特点
f.eat();//调用子类重写方法
//f.sleep();// Fu类中没有定义sleep方法,那么会报出编译错误
}
}
2.4多态中静态的访问特点
编译看左边,运行看左边
- 编译看左边 : 如果等号左边的类中,没有定义这个静态的成员(成员变量和方法),那么调用这个静态就会报错
- 运行看左边 : 代码调用的静态,根据等号左边类中定义的静态成员的运行结果为准
原因 : 静态属于类,不属于任何对象
代码
// 定义一个父类
public class Fu{
public static void speak(){
System.out.println(“父类的静态speak”);
}
}
// 定义子类,继承父类
public class Zi extends Fu {
// 子类中可以继承到父类中的静态方法 speak,子类自己重新定义了speak方法
public static void speak(){
System.out.println(“zi类的静态speak”);
}
}
// 测试多台态的访问特征
public class TestFu{
public static void main(String[] args) {
Fu f = new Zi();
// 3. 静态在多态中的访问特点,编译运行全看等号左边
f.speak();//父类的静态speak
}
}
2.5多态的转型
- 向上转型 : 父类的引用指向子类的对象,向上转型(变量f只能使用子父类同有的方法)
Fu f = new Zi();
实质 : 使用范围在缩小,子类中的特有的成员变量和方法,全部不能调用
理解 : Zi年轻,Fu年长,年轻------>年长---->年龄增长,向上 - 向下转型
要求: 在多态的使用场景下,想要使用子类中的特有的成员变量和方法
通过多态的向下转型实现
公式: 将父类的引用最终还原成一个子类的类型
子类的类型 变量名 = (子类的类型)父类的引用;
Fu f = new Zi(); // 多态,向上转型
Zi z = (Zi)f;
代码
// 定义一个父类Person
public class Person {
public void work(){
System.out.println(“人类需要工作”);
}
}
// 医生类继承Person类,因为医生也是人类的一种表现形式
public class Doctor extends Person{
public void work(){
System.out.println(“医生治病求人”);
}
// 医生这个子类的特有方法,在父类Person中没有的
public void study(){
System.out.println(“医生需要学习”);
}
}
public class TestPerson{
public static void main(String[] args) {
// 创建一个医生对象
Doctor d = new Doctor();
//多态
Person p1 = new Doctor();
p1.work(); // 医生治病求人
// 使用多态的向下转型 : 可以调用子类中特有的变量和方法
Doctor doc = (Doctor)p1;
doc.study();
}
}
2.6多态的好处和使用
封装 : 好处,安全性高,复用性
继承 : 复用性,可维护性
多态 : 扩展性
多态扩展性的使用场景 : 定义一个功能,将功能的参数类型,设定为一个父类的类型,这个功能在实际调用的时候,父类的本身以及该父类的所有的子类,都可以作为方法的实际参数进行传递
动物吃饭案例:
定义一个Animal类,动物都能吃
动物有子类 cat , dog
定义一个测试类,测试类中: 定义一个动物吃饭功能,要求不同的动物吃不同的饭
代码
public class Animal {
public void eat(){
}
}
public class cat extends Animal{
public void eat(){
System.out.println(“猫吃猫粮”);
}
}
public class dog extends Animal {
public void eat(){
System.out.println(“狗啃骨头”);
}
}
public class TestAnimal{
//定义一个方法,能让不同的动物,吃不同饭
public void animalEat(Animal a){// Animal a = new cat();
a.eat();
}
public static void main(String[] args) {
TestAnimal ta = new TestAnimal();
// 需要一个Animal类型的参数
Animal a = new cat();
ta.animalEat(a);//new cat();
Animal a1 = new dog();
ta.animalEat(a1);
}
}
榨汁机案例:
要求 : 定义一个榨汁机类,类中有一个方法,榨汁,榨汁功能,能榨苹果汁,也能榨橙汁,任何谁都可以榨汁
代码
// 水果类,可以作为所有类的父类
public class Fruit{
public void flow (){
System.out.println(“榨汁”);
}
}
// 定义一个水果,苹果
public class Apple extends Fruit{
@Override
public void flow (){
System.out.println(“苹果汁”);
}
}
// 定义一个水果,叫橙子
public class Orange extends Fruit{
@Override
public void flow(){
System.out.println(“橙子汁”);
}
}
// 定义一个榨汁机类
public class JuiceMachine{
// 如果将一个父类作为方法的参数
public void makeJuice(Fruit f){ // Fruit f = new Apple();
// 不同的水果,出不同的果汁
f.flow();
}
public void getInt(int i){// int i = 9
}
/*public void makeJuice(Orange o){
o.flow();
}*/
public static void main(String[] args) {
JuiceMachine jm = new JuiceMachine();
// makeJuice 方法需要一个参数,类型Fruit
// 1.多态
Fruit f = new Apple();
jm.makeJuice(f);// 苹果汁 new Apple();
Fruit f1 = new Orange();
jm.makeJuice(f1);// 橙子汁
// 2. 多态的应用
Apple a = new Apple();
jm.makeJuice(a);// 苹果汁 new Apple();
// 需要int类型参数
int i = 9 ;
jm.getInt(i);//实际传值为9
}
}
- 抽象类
抽象 : 就是无法描述的
3.1 抽象方法
抽象方法 : 只有方法的声明,没有方法的实现主体
普通方法定义:
修饰符 返回值类型 方法名(参数列表){
// 方法逻辑
return ;
}
抽象方法的定义:
修饰符 abstract 返回值类型 方法名(参数列表) ; ----- > 抽象方法
abstract : 关键字,抽象的含义
代码
// 抽象的定义
public class AbstractDemo {
// 定义一个抽象方法
public abstract void eat();
}
3.2 抽象类
抽象方法需要存在于抽象类中
抽象类的定义:
修饰符 abstract class 类名{
// 可以定义抽象方法
}
代码
// 抽象的定义
public abstract class AbstractDemo {
// 定义一个抽象方法
public abstract void eat();
}
3.3抽象的特点
- 抽象方法和抽象类都使用abstract关键字修饰的
public abstract void eat(); ---->抽象方法
public abstract class 类名{}----->抽象类 - 抽象方法必须要存在于抽象类中
抽象类中不一定含有抽象方法 - 抽象类不能实例化对象,new对象不行
- 抽象类和抽象方法存在的意义:
- 抽象类存在的意义,等着被继承,等着当父类
- 抽象方法存在的意义,等着被重写
- 抽象方法是可以被子类继承到的,抽象类的子类,如果没有将父类中的所有抽象方法全部重写,那么这个子类仍然是个抽象类
- 如果子类将父类中的所有抽象方法全部重写,这个子类及时一个普通的类,可以实例化对象,可以调用重写的方法
为什么要有抽象: 做规则的限定
例如 : Animal类,每个动物都需要吃饭,但是Animal不知道如何实现eat吃这个功能,Animal就将eat定义为抽象方法,Animal类就变成抽象类. 现在Animal的所有子类,必须吃饭,必须实现eat方法,才能成为一个正常的动物
代码
// 抽象的定义
public abstract class AbstractDemo {
// 定义一个抽象方法
public abstract void eat();
public static void main(String[] args) {
// 抽象类不能实例化对象
// 原因: 抽象类中,可能含有抽象方法,抽象方法因为没有方法体,
// 不能运行
// 如果抽象类可以实例化对象,那么就可以通过对象名.调用抽象方法运行
// 抽象方法不能运行,矛盾了
//AbstractDemo ad = new AbstractDemo();
}
}
// 抽象类AbstractDemo的子类
public class AbstractDemoSub extends AbstractDemo{
// AbstractDemoSub 继承了抽象父类AbstractDemo里面的抽象方法eat
public void eat(){
System.out.println(“子类重写了父类的抽象方法”);
}
public static void main(String[] args) {
AbstractDemoSub as = new AbstractDemoSub();
as.eat();
System.out.println(“Hello World!”);
}
}
3.4抽象方法的重写
public abstract void eat();
子类重写抽象方法的注意事项:
- abstract 抽象关键字,去掉
- 方法后面的; 去掉
- 给方法添加上方法体{}
public void eat(){
// 子类可以任意的实现重写方法内容
System.out.println(“子类重写了父类的抽象方法”);
}
3.5抽象类的成员
将抽象类先当成一个普通的类看待,只不过比普通的类可以多定义抽象方法
- 成员变量
- 构造方法 : 给成员变量赋值,通过什么鉴定一个文件是否可以定义构造
看这个文件中能不能定义变量,如果可以定义成员变量,就可以具有构造方法 - 普通方法
- 静态方法 : 静态属于类,与对象无关
- 抽象方法
代码
// 抽象的定义
public abstract class AbstractDemo {
int i = 10 ;
// 定义一个抽象方法
public abstract void eat();
public static void sleep(){
System.out.println("sleep");
}
public static void main(String[] args) {
// 抽象类不能实例化对象
// 原因: 抽象类中,可能含有抽象方法,抽象方法因为没有方法体,
// 不能运行
// 如果抽象类可以实例化对象,那么就可以通过对象名.调用抽象方法运行
// 抽象方法不能运行,矛盾了
//AbstractDemo ad = new AbstractDemo();
}
}
- 接口