Java基础-2(抽象,接口,多态,final关键字)

13.抽象
  • 概念例子
    在这里插入图片描述

创建,使用示例如下:

/*
抽象方法:就是加上abstract关键字,然后去掉大括号,直接分号结束。
抽象类:抽象方法所在的类,必须是抽象类才行。在class之 前写上abstract即可。

如何使用抽象类和抽象方法:
1.不能直接创建new抽象类对象。
2.必须用一个子类来继承抽象父类。
3.子类必须覆盖重写抽象父类当中所有的抽象方法。
覆盖重写(实现):去掉抽象方法的abstract关键字,然后补上方法体大括号。
4.创建子类对象进行使用。
*/
public abstract class Animal {
    //这是一个抽象方法,代表吃东西,但是具体吃什么(大括号的内容)不确定。
    public abstract void eat();

    //这是普通的成员方法,抽象类中是可以具体实现成员方法的
    public void normalMethod() {
    }
}

public class Cat extends Animal{

    @Override
    public void eat() {
        System.out.println("猫吃鱼");
    }
}

public class DemoMain {
    public static void main(String[] args) {
        Cat cat=new Cat();
        cat.eat();
    }
}
  • 注意事项:

1.抽象类不能创建对象.如果创建,编译无法通过而报错。只能创建其非抽象子类的对象。

理解: 假设创建了抽象类的对象,调用抽象的方法,而抽象方法没有具体的方法体,没有意义。

2.抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的。

理解: 子类的构造方法中,有默认的super0, 需要访问父类构造方法。

3.抽象类中,不定包含抽象方法.但是有抽象方法的类必定是抽象类。

理解:未包含抽象方法的抽象类,目的就是不想让调用者创建该类对象,通常用于某些特殊的类结构设计.

4.抽象类的子类,必须重写抽象父类中所有的抽象方法,暂则,编译无法通过而报错。除非该子类也是抽象类。

理解:假设不重写所有抽象方法,则类中可能包含抽象方法。那么创建对象后,调用抽象的方法,没有意义。
14.接口
  • 简单来说即多个类的公共规范,是一种引用数据类型,最重要的内容就是其中的抽象方法
  • 定义格式
public interface 接口名称{
	//接口内容
}

备注:换成了关键字interface之后,编译生成的字节码文件仍然是: .java --> .class.
如果是Java 7,那么接口中可以包含的内容有:

1.常量;2.抽象方法

如果是Jova 8,还可以额外包含有:

3.默认方法;4.静态方法.

如果是Java 9,还可以额外包含有:

5.私有方法
  • 示例
/*
在任何版本的Jova中,接口都能定义抽象方法。
格式:
public abstract 返回值类型方法名称(参数列表);
注意事项:
1.接口当中的抽象方法,修饰符必须是两个固定的关键字。public. abstract
2.这两个关键字修饰符,可以选择性地省略。(今天刚学, 所以不推荐。)
3.方法的三要素,可以随意定义。
 */
public interface MyInterfaceAbstract {
    //这是一个抽象方法
    public abstract void methodAbs1();
    //这也是抽象方法
    abstract void methodAbs2();
    //这也是抽象方法
    public void methodAbs3();
    //这也是抽象方法
    void methodAbs4();
}

使用:

public class MyInterfaceAbstractImpl implements MyInterfaceAbstract{
    @Override
    public void methodAbs1() {
        System.out.println("这是第一个方法");
    }

    @Override
    public void methodAbs2() {
        System.out.println("这是第二个方法");
    }

    @Override
    public void methodAbs3() {
        System.out.println("这是第三个方法");
    }

    @Override
    public void methodAbs4() {
        System.out.println("这是第四个方法");
    }
}

/*
接口使用步骤:
1.接口不能直接使用,必须有一个“实现类“崃“实现"咳接口。
格式:
public class实现类名称implements 接口名称{
// ...
}
2.接口的实现类必须覆盖重写(实现)接口中所有的抽象方法。
实现,去掉abstract关键字,加上方法体大括号。
3.创建实现类的对象,进行使用。

注意事项:
如果实现类并没有覆盖重写接口中所有的抽象方法,那么这个实现类自己就必须是抽象类。

*/
public class InterfaceDemo01 {

    public static void main(String[] args) {
        // 错误写法!不能直接new接口对象使用。
        // MyInterfaceAbstract inter = new MyInterfaceAbstract();
        //创建实现类的对象使用
        MyInterfaceAbstractImpl impl = new MyInterfaceAbstractImpl();
        impl.methodAbs1();
        impl.methodAbs2();
    }
}

  • 接口的默认方法

从Java 8开始,接口里允许定义默认方法。
格式:
public default 返回值类型方法名称(参数列表) {
方法体
}
备注:接口当中的默认方法,可以解决接口升级的问题。即实现接口的类已经投入使用,但是接口又新添加了抽象方法,则此时实现接口的类也需要重新更改,为了解决这个问题,可以使用默认方法,避免修改。

例子:

public interface MyInterfaceDefault {
	//抽象方法
	public abstract void methodAbs();
	//新添加了一个抽象方法
	//public abstract void methodAbs2();
	//新添加的方法,改成默认方法
	publid default void methodDefault() {
		System.out.print1n( "这是新添加的默认方法");
	}
}

public class MyInterfaceDefaultA implements MyInterfaceDefault {
	@Override
	public void methodAbs(){
		System.out.println("实现了抽象方法,AAA");
    }
}

public class MyInterfaceDefaultB implements MyInterfaceDefault {
	@Override
	public void methodAbs(){
		System.out.println("实现了抽象方法,BBB");
    }
}

public class Demo02Interface {
	public static void main(String[] args) {
		//创建了实现类对象
		MyInterfaceDefaultA a = new MyInterfaceDefaultA(); 
		a.methodAbs();  //调用抽象方法,实际运行的是右侧实现类。
       
        //调用默认方法,如果实现类中没有,会向上找接口
		a.methodDefault();
	}
}

注意事项

1.接口的默认方法,可以通过接口实现类对象,直接调用

2.接口的默认方法,也可以被接口实现类进行覆盖重写。


  • 接口的静态方法

从Java 8开始,接口当中允许定义静态方法。
格式:
public static 返回值类型方法名称(参数列表) {
方法体
​ }
提示:就是将abstrac t或者defaul t换成static即可,带上方法体。
**使用:**与类中的静态方法一致,即“接口名称.静态方法名”

public interface MyInterfaceStatic {
    
	public static void methodStatic(){
		System.out.println("这是接口的静态方法!");
	}
}
public class MyInterfaceStaticImpl implements MyInterfaceStatid { 
}

/*
注意事项:不能通过接口实现类的对象来调用接口当中的静态方法。
正确用法。通过接口名称,直接调用其中的静态方法。
格式:
接口名称.静态方法名(参数);
*/
public class Demo03Interface {
	public static void main(String[] args) {
	//创建了实现类对象,为了对比而实现,实际上不需要创建,直接调用接口静态方法即可
	MyInterfaceStaticImpl impl = new MyInterfaceStaticImpl();
	//错误写法!原因:如果这个类实现多个接口,接口的静态方法可能重名
	//impl.methodstatic();
	//直接通过接口名称调用静态方法
	MyInterfaceStatic.methodStotic();
	}
}

  • 接口的私有方法

当接口中需要抽取一个共有方法用来解决重复代码问题时,但又需要这个共有方法不可以被实现类使用,即需要将这个共有方法私有化。

从Java 9开始, 接口当中允许定义私有方法。
1.普通私有方法,解决多个欧认方法之间重复代码问题
格式:

private 返回值类型方法名称(参数列表) {
               方法体
}

2.静态私有方法,解决多个静态方法之间重复代码问题
格式:

private static 返回值类型方法名称(参数列表) (
               方法体
}
public interface MyInterfacePrivateA {
	public default void methodDefault1() {
		System. out . print1n("默认方法1");
		methodCommon();
	}
	public default void methodDefault2() {
		System. out .print1n(“默认方法2");
	methodCommon();
	}
 	public defJu1t void methodCommon() {
		System.out.print1n("AAA");
		System.out.print1n("BBB");
		System.out.print1n("CCc");
	}
}
public class MyInterfacePrivateAImpl implements MyInterfacePrivateA {
	public void methodAnother(){
		// 直接访问到了接口中的默认方法,这样是错误的!
		//methodCommon();
	}
}

public interface MyInterfacePrivateB {
	public static void methodStatic1() {
	System. out . println("静态方法1");
		methodstaticCommon();
	}
	public static void methodStatic2() {
		System. out .print1n(“静态方法2");
		methodStaticCommon();
	}
	private static void methodStaticCommon() {
		System. out . print1n("AAA");
		System. out . print1n("BBB");
		System.out. print1n("CCC");
	}
}
public class Demo04Interface [
	public static void main(String[] args) {
		MyInterfacePrivateB.methodStatic1();
		MyInterfacePrivateB.methodStatic2();
		//错误写法!
		MyInterfacePrivateB. methodStaticCommon();
	}
}


  • 接口的常量

接口当中也可以定义“成员变量”。但是必须使用public static final三个关键字进行修饰。
从效果上看,这其实就是接口的[常量]。
格式:

public static final 数据类型常量名称=数据值;

备注:
一旦使用final关键字进行修饰,说明不可改变。

注意

1.接口当中的常量,可以省略public static final,注意,不写也默认有这些关键字。
2.接口当中的常量,必须进行赋值;不能不赋值。
3.接口中常量的名称,使用完全大写的字母,用下划线进行分隔。(推荐命名规则)

public interface MyInterfaceConst {
	//这其实就是一个常量,一旦赋值,不可以修改
	public static final int NUM=10;
}

  • 接口使用

使用接口的时候,需要注意:
1.接口是没有静态代码块或者构造方法的。

2.一个类的直接父类是唯一的,但是一个类可以同时实现多个接口。
格式:

public class MyInterfaceImpl implements MyInterfaceA, MyInterfaceB {
		   // 覆盖重写所有抽象方法
}

3.如果实现类所实现的多个接口当中,存在重复的抽象方法,那么只需要覆盖重写一次即可。

4.如果实现类没有覆盖重与所有接口当中的所有抽象方法,那么实现类就必须是一个抽象类。

5.如果实现类锁实现的多个接口当中,存在重复的默认方法,那么实现类一定要对冲突的默认方法进行覆盖重写。

6.一个类如果直接父类当中的方法,和接口当中的默认方法产生了神突,优先用父类当中的方法。

public interface MyInterfaceA {
	//错误写法!接口不能有静态代码块
	//static {   }
	//错误写法!接口不能有构造方法
	//public MyInterfaceA() {   }
	public abstract void methodA() ;
	public abstract void methodAbs();
    public default void methodDefault( ) {
		System. out . println( "默认方法A" );
	}
}
public interface MyInterfaceB {
	public abstract void methodB() ;
	public abstract void methodAbs();
    
    public default void methodDefault( ) {
		System. out . println( "默认方法B" );
	}
}

public class MyInterfaceImpl /*extends object*/ implements MyInterfaceA, MyInterfaceB {
	@Override
	public void methodA() {
		System.out.println( "覆盖重写了A方法");
    }
	@Override
	public void methodB() {
		System.out.println("覆盖重写了B方法");
    }
	@Override
	public void methodAbs() {
		System.out.println( "覆盖重写了AB接口都有的抽象方法");
	}
	@Override
	public void methodDefault() {
		System.out.println("对多个接口当中神突的默认方法进行了覆盖重写");
    }
}


  • 小结

在Java 9+版本中,接口的内容可以有: .
1.成员变量其实是常量,格式:
[public] [static] [final] 数据类型常量名称=数据值;
注意:
常量必须进行赋值,而且一旦赋值不能改变。
常量名称完全大写,用下划线进行分隔。

2.接口中最重要的就是抽象方法,格式:
[public] [abstract] 返回值类型方法名称(参数列表);
注意:实现类必须覆盖重写接口所有的抽象方法,除非实现类是抽象类。

3.从Java 8开始,接口里允许定义默认方法,格式:
[public] default 返回值类型方法名称(参数列表) {方法体}
注意:默认方法也可以被覆盖重写;default无法省略

4.从Java 8开始,接口里允许定义静态方法,格式: .
[public] static 返回值类型方法名称(参数列表) {方法体}
注意:应该通过接口名称进行调用,不能通过实现类对象调用接口静态方法

5.从Java 9开始,接口里允许定义私有方法,格式:
普通私有方法: private 返回值类型方法名称(参数列表) {方法体}
静态私有方法: private static 返回值类型方法名称(参数列表) {方法体}
注意: private的方法只有接口自己才能调用,不能被实现类或别人使用。


  • 接口之间的多继承

1.类与类之间是单继承的。直接父类只有一一个。
2.类与接口之间是多实现的。一个类可以实现多个接口。
3.接口与接口之间是多继承的。
注意事项:
1.多个父接口当中的抽象方法如果重复,没关系。
2.多个父接口当中的默认方法如果重复,那么子接口必须进行默认方法的覆盖重写,而且带着default关键字

public interface MyInterfaceA {
	public abstract void methodA( ) ;
	public abstract void methodCommon( );
}
public interface MyInterfaceB {
	public abstract void methodB( ) ;
	public abstract void methodCommon( );
}
/*
这个子接口当中有几个方法?答: 4个。
methodA 来源于接口A
methodB 来源于接口B
methodCommon 同时来源于接口A和B
method来源于我自己
*/
public interface MyInterface extends MyInterfaceA, MyInterfaceB {
	public abstract void method();
}
15.多态
  • 一个对象拥有多种形态,即对象的多态性
    在这里插入图片描述
  • 格式和使用

代码当中体现多态性,其实就是一句话:父类引用指向子类对象。
格式:

父类名称对象名= new子类名称();

或者:

接口名称对象名= new 实现类名称();
public class Fu {
	public void method() {
		System.out.println("父类方法");
	}
    public void methodFu() {
		System.out.println("父类特有方法");
	}
}

public class Zi extends Fu {
	@Override
	public void method( ) {
		System.out.println("子类方法");
	}
}

public class Demo01Multi {
	public static void main(String[] args) {
		//使用多态的写法
		//左侧父类的引用,指向了右侧子类的对象
		Fu obj = new Zi();
        
        obj.method();
		obj.methodFu() ;
	}
}
  • 多态中的成员变量使用特点

正常访问成员变量的两种方式:
1.直接通过对象名称访问成员变量:看等号左边是谁,优先用谁,没有则向上找。
2.间接通过成员方法访问成员变量:看该方法属于谁,优先用谁, 没有则向上找。

多态中访问成员变量的方式和正常访问方式一致(成员变量无法覆盖重写,只有方法才可以)

pub1ic class Fu {
	int num = 10;
    public void showNum( ) {
		System. out . print ln(num);
	}
}
public class Zi extends Fu {
	int num = 20;
    public void showNum( ) {
		System.out.print ln(num);
	}
}
public class Demo01MultiField {
	public static void main(String[ ] args) {
		// 使用多态的写法,父类引用指向子类对象
		Fu obj = new Zi();
        //等号左边是父类,优先找父类中的成员变量,没有则向上找,不会向下找
		System.out.println(obj.num);
        //子类没有覆盖重写,就是父: 10
        //子类如果覆盖重写,就是子: 20
        obj.showNum(); 

	}
}
  • 成员方法的使用特点

在多态的代码当中,成员方法的访问规则是:看new的是谁,就优先用谁,没有则向上找。

口诀:编译看左边,运行看右边。

对比一下:
成员变量:编译看左边,运行还看左边
成员方法:编译看左边,运行看右边

pub1ic class Fu {
	int num = 10;
	public void showNum() {
		System.out.println(num);
	}
	public void method() {
		System.out.println("父类方法");
	}
	public void methodFu() {
		System.out.println("父类特有方法");
	}
}

public class Zi extends Fu {
	int num = 20;
	int age = 16;
	@Override
	public void showNum() {
		System. out . println(num); .
	}
	@Override
	public void method() {
		System. out . println("子类方法");
	}
	public void methodZi() {
		System. out . print1n("子类特有方法");
	}
}

public class Demo02MultiMethod {
	public static void main(String[] args) {
		Fu obj=new Zi(); //多态
		obj.method(); //父子都有,优先用子
		obj.methodFu(); //子类没有,父类有,向上找到父类
        
        //编译看左边,左边是Fu,Fu当中没有methodZi方法,所以编译报错。
       //obj.methodZi();//错误写法
	}
}

  • 多态的好处
    在这里插入图片描述
  • 对象的向上转型和向下转型

1.对象的向上转型,其实就是多态写法:
格式:父类名称 对象名= new 子类名称:
含义:右侧创建一个子类对象,把它当做父类来看待使用。
注意事项:向上转型一定是安全的。

弊端:对象一旦向上转型为父类,那么就无法调用子类原本特有的内容。
public abstract klass Animal {
	public abstract void eat();
}
public class Cat extends Animal {
	@Override
	public void eat( ) {
		System.out.println( "猫吃鱼");
	}
    public void catchMouse(){
        System.out.println( "猫捉老鼠");
    }
}
public class Demo01Main {
	public static void main(String[ ] args) {
		//对象的向上转型,就是:父类引用指向之类对象。
		Animal animal = new Cat();
		animal.eat() ;
	}
}

解决方案:对象的向下转型

2.对象的向下转型,其实是一个[还原]的动作。

格式:子类名称对象名= (子类名称)父类对象;

含义:将父类对象,
[还原]成为本来的子类对象。
Animal animal = new Cat0; //本来是猫,向上转型成为动物
Cat cat = (Cat) animal; //本来是猫,已经被当做动物了,还原回来成为本来的猫
注意事项:
a.必须保证对象本来创建的时候,就是猫,才能向下转型成为猫。
b.如果对象创建的时候本来不是猫,现在非要向下转型成为猫,就会报错(ClassCastException)。

这部分代码是结合上面一部分的代码的

public class Dog extends Animal {
	@Override
	public void eat() {
		System.out.println( "狗吃SHIT");
	}
	public void watchHouse() {
		System.out.println( "狗看家");
	}
}

pub1ic class Demo01Main {
	public static void main(String[] args) {
		//对象的向上转型,就是:父类引用指向之类对象。
		Animal animal = new Cat(); //本来创建的时候是- -只猫
		animal.eat(); //猫吃鱼
		
		//animal.catchMouse(); //错误写法!
		
		//向下转型,进行“还原”动作
		Cat cat = (Cat)animal;
		cat.catchMouse();
        //错误的向下转型	
        //错误的写法,编译不会报错,但是运行会出现异常
        //java.Lang.ClassCastException.类转换异常
        Dog dog = (Dog)animal;
	}
}

在这里插入图片描述

  • instanceof关键字

如何才能知道一个父类引用的对象,本来是什么子类?
格式:

对象instanceof 类型

这将会得到一个boolean值结果,也就是判断前面的对象能不能当做后面类型的实例。
下面的代码块和上面两个代码块关联

public class Demo02Instanceof {
	public static void main(String[] args){
		Animal animal = new Cat(); //本来是- -只猫
		animal.eat(); //猫吃鱼
		//如果希望掉用子类特有方法,需要向下转型
		//判断一下父类引用animal本来是不是Dog
		if (animal instanceof Dog) {
			Dog dog = (Dog) animal;
			dog.watchHouse() ;
		}
		//判断一下animal本来是不是Cat
		if (animal instanceof Cat) {
			Cat cat = (Cat) anlmal;
			cat.catchMouse();
		}
        giveMeAPet(new Dog());
    }
	public static void giveMeAPet(Animal animal) {
		if (animal instanceof Dog) {
			Dog dog = (Dog) animal;
			dog . watchHouse();
		}
		if (animal instanceof Cat){
			Cat cat = (Caf) animal;
			cat . catchMouse();
        }
    }
}
16 final关键字
  • final关键字代表最终、不可改变的。
  • 常见四种用法:
    1.可以用来修饰一个类
    2.可以用来修饰一个方法
    3.可以用来修饰一个局部变量
    4.可以用来修饰一个成员变量
  • 格式:

1.当final关键字用来修饰一个类的时候,格式:

public final class类名称{
 //...
}

含义:当前这个类不能有任何的子类。(太监类)
注意:一个类如果是final的,那么其中所有的成员方法都无法进行覆盖重写。但可以有父类

2.当final关键字用来修饰一个方法的时候,这个方法就是最终方法,也就是不能被覆盖重写。

格式:
修饰符final 返回值类型方法名称(参数列表) {
//方法体
}

注意事项:对于类,方法来说,abstract关键字和final关键字不能同时使用,因为矛盾。

3.一旦使用final用来修饰局部变量,那么这个变量就不能进行更改。简记:“一次赋值,终生不变”

public class Demo01Final {
	public static void main(String[] args) {
		int num1 = 10;
		System. out. println(num1); // 10
		num1 = 20;
		System. out . println(num1); // 20

		final int num2 = 200;
		System.out.println(num2); // 200
		//num2 = 250; //错误写法!不能改变!
		//num2 = 200; //错误写法!就算值一样也无法更改
        
		//正确写法!只要保证有唯一次赋值即可
		final int num3;
		num3 = 30;
        
        //对于基本类型来说,不可变说的是变量当中的数据不可改变
		//对于引用类型来说,不可变说的是变量当中的地址值不可改变
		Student stu1 = new Student( name: “赵丽颖");
		System. out . println(stu1);
		System. out . println(stu1. getName()); //赵丽颖
		stu1 = new Student( name: "霍建华");
		System. out . println(stu1);
		System. out . println(stu1. getName()); //霍建华
		System. out. print1n("=============");
		final Student stu2 = new Student( name: "高圆圆");
		//错误写法! final的引用类型变量,其中的地址不可改变
		//stu2 = new Student( name: "赵又廷");
	}
}

对于基本类型来说,不可变说的是变量当中的数据不可改变
对于引用类型来说,不可变说的是变量当中的地址值不可改变

4.对于成员变量来说,如果使用final关键字修饰, 那么这个变量也照样是不可变。
1).由于成员变量具有默认值,所以用了final之后必须手动赋值,不会再给默认值了。
2).对于final的成员变量,要么使用直接赋值,要么通过构造方法赋值。二者选其一。
3).必须保证类当中所有重载的构造方法,都最终会对final的成员变量进行赋值。

public class Person {
	private final String name;
    //private final String name="xx";
	public Person() {
		name="yy";
	}
	public Person(String name) {
		this. name = name;
	}
	public String getName() {
		return name;
	}
}

17.四种权限修饰符总结

Java中有四种权限修饰符:

Y可访问/N不可访问public>protect>(default)>private
同一个类(自己)YYYY
同一个包(邻居)YYYN
不同包子类(儿子)YYNN
不同包非子类(陌生人)YNNN
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值