抽象、接口

抽象

使用场景

多个子类的共性方法存在差别,父类无法具体描述时~使用抽象方法和抽象类(抽象方法所在的类)

定义方式

  • 抽象方法: 用abstract修饰且没有方法体,所在类必须为抽象类
    不能被 static 、private、final 修饰,只能被 abstract 、public 、 protected、[default]修饰(默认不写即 default 也可以)
  • 抽象类: 用abstract修饰且包含抽象方法
    可以 没有抽象方法
    可以有 非抽象方法
    不能实例化
    应当是一个父类
    可以有 构造函数 和重载构造函数
public abstract class 类名{
		public abstract 返回类型 方法名(参数1,参数2... ...);		
}

使用方式

  • 第一种
    创建自定义类继承抽象类
    重写父类的所有抽象方法
    创建子类对象,调用方法
  • 第二种
    创建自定义抽象类继承抽象类

权限修饰符

修饰对象:成员方法/变量( 不能修饰局部

可访问范围
假设:demo包中的A类的一个成员方法/变量publicprotected[default]private
同一个类中demo包中的A类中的成员方法中,尝试直接【用变量名/方法名】调用A的变量/方法
同包不同类中demo包中的B类中,尝试【创建A类的对象,对象.变量 或者 对象.方法 】调用A的变量/方法×
不同包中子类Test包中的B类中,B extends A,尝试直接【用变量名/方法名】调用A的变量/方法××
不同包中无关类Test包中的B类中,尝试【创建A类的对象,对象.变量 或者 对象.方法 】调用A的变量/方法×××
总结public>protected>[default]>private同工程可见有关系(继承/同包)可见包内可见仅类内可见
使用场景希望对外提供的(如私有属性的set/get方法)
②工具类中的方法(如Math.PI、Math.pow())
需要确保子类的访问的内容
(抽象类的成员访问权限至少是protected)
较少使用
包内各类间关联性强,需要互相访问,而包外不允许访问时
属性的封装(类外通过get/set安全访问)一般不修饰方法(一旦私有化类外完全无法访问)
工具类中的辅助数据:当变量不是服务于前端调用者,而只是功能实现的过程时,需对其私有化避免被随意调用

匿名

String[] names={"张三","李四","王五"};
System.out.println(names[new Random().nextInt(names.length)]);
//Random类的对象没有对象名,即匿名的(只有地址值);

优点

  • 节省代码

缺点

  • 一次性
    只能使用一次,无法再次找到这个对象,对其操作(关闭资源、修改值等)
  • 代码可读性差

使用场景

  • 推荐用于 想要调用某个对象的方法仅调用一次的)
  • 不推荐用于属性

接口 interface

概述

  • ≈ 仅包含抽象方法的抽象类

  • 优点
    提高扩展性
    对外提供了统一的标准和准则

  • 格式

      //定义一个接口
      public interface 接口名{
      
      }
      //实现一个接口
      public class 类名 implements 接口名{
      
      }
    
  • 特点

成员特点
成员变量默认 public、static、final
成员方法默认 public、abstract
java1.8及以后可以有非抽象方法(使用default声明,不写默认public)——为实现类提供共性方法
java1.8及以后可以有静态的方法(使用static声明,不写默认public)——为了方便调用
java1.9及以后可以有私有方法(使用private声明)——为了辅助本接口功能的运行(为了本接口的静态/普通方法的实现)
接口不能被实例化(抽象)

默认修饰符的原因
static、abstract、final——强制调用者必须重写、变量统一,即遵循接口的规则;

使用

  • 自定义实现类,实现接口
  • 实现接口的所有抽象方法
  • 创建实现类对象,调用方法(接口的成员变量可以直接用接口名调用)
public interface D {//定义一个接口
	int i=0;//接口的成员变量,默认public static final的
	void f() {//接口的成员方法,默认public abstract的
		//System.out.println("D接口的f方法"); //不能写,会报错,大括号都不能写
	}
	default void e() {//接口的成员方法,java1.8及以后可以手动声明非抽象方法,可以被重写,默认public
		System.out.println("java1.8及以后的版本,D接口也可以有default静态方法了");
	}
	static void g() {//接口的成员方法,java1.8及以后可以手动声明静态方法,不能被重写,默认public
		System.out.println("java1.8及以后的版本,D接口也可以有静态方法了");
	}
	private void h() {//接口的成员方法,java1.9及以后可以手动声明私有方法,不能被重写
		System.out.println("java1.9及以后的版本,D接口也可以有私有方法了");
	}
}
public class T implements D{//实现类实现接口
	System.out.println(D.i);//可以直接调用接口的成员方法
	System.out.println(D.i++);//final的变量i,不能做++运算
	@Override//必须实现接口的抽象方法
	public void f() {//只能是public的,因为接口的方法默认public的,重写的范围修饰符只能大于等于原方法;
		System.out.println("实现类重写/实现接口的抽象方法");
	}
}
public class Test1 {
	public static void main(String[] args) {
		D d=new T();//多态写法,一个接口无法创建对象,但一个接口的实现类的对象也是接口的对象
		d.e();
		d.g();
		//d.h();//会报错
		
	}
}

多层继承/实现关系

接口可以实现多“继承”

	一个实现类实现多个接口
	一个接口可以同时继承多个接口
	实现类可以继承类的同时实现接口
定义接口D
-----------------------------------------------------------------------
public interface D {
	int i=1;
	void f(); 
	void g(); 
	void a(); 
	default void h(){};
	default void j(){};
}
定义接口E
-----------------------------------------------------------------------
public interface E {
	int i =2;
	void f(); 
	default void h(){};
}
定义父类A
-----------------------------------------------------------------------
public class A {
	int i=3;
	public void a() {//父类中已经有方法与接口的抽象方法同名,子类继承到了这个方法,等于已经实现了那个抽象方法,故无需重写D的a方法
		
	}
}
定义实现类E实现接口D和E
-----------------------------------------------------------------------
public class T extends A implements D,E{
	static int i=0;
	public static void main(String[] args) {
		System.out.println(D.i);
		System.out.println(E.i);
		System.out.println(i);
	}
	
	@Override//抽象方法重名,重写一次就好,反正没有方法体
	public void f() {
		System.out.println("实现类T重写的(D、E接口的重名抽象方法)F方法");
		
	}
	@Override//抽象方法不重名,也需要重写
	public void g() {
		System.out.println("实现类T重写的(D接口的抽象方法)G方法");
		
	}

	@Override//默认普通方法不重名不需要重写,重名才需要重写
	public void h() {
		System.out.println("实现类T重写的(D接口的默认方法)H方法");
		D.super.h();//如果想要使用重名普通方法,需明确使用的是哪个父类的,在前面加D.或者E.
	}
}

测试类创建实现类对象查看调用情况
-----------------------------------------------------------------------
public class Test1 {
	public static void main(String[] args) {
		D td=new T();
		E te=new T();
		T t=new T();
		System.out.println(td.i);//1
		System.out.println(te.i);//2
		System.out.println(t.i);//0
		td.f();//实现类T重写的(D、E接口的重名抽象方法)F方法
		td.g();//实现类T重写的(D接口的抽象方法)G方法
		td.h();//实现类T重写的(D接口的默认方法)H方法
		td.j();//无反应。D接口的j默认方法没有写方法体内容
		td.a();//无反应。父类A的a默认方法没有写方法体内容
		te.f();//实现类T重写的(D、E接口的重名抽象方法)F方法
		//te.g();//编译报错,编译看左边,E类没有g方法
		te.h();//实现类T重写的(D接口的默认方法)H方法
		//te.j();//编译报错,编译看左边,E类没有j方法
		//te.a();//编译报错,编译看左边,E类没有a方法
		t.f();//实现类T重写的(D、E接口的重名抽象方法)F方法
		t.g();//实现类T重写的(D接口的抽象方法)G方法
		t.h();//实现类T重写的(D接口的默认方法)H方法
		t.j();//无反应。D接口的j默认方法没有写方法体内容
		t.a();//无反应。父类A的a默认方法没有写方法体内容
	}
}

实现类/子接口实现的多个接口间存在:

  • 重名方法
    • 实现类将抽象方法重写一次即可;子接口无需重写(等其实现类来重写)
    • 非抽象方法实现类/子接口都需要重写(因为会引起歧义)(子接口被实现类调用时,会根据就近原则,默认调用到最外层的接口中的方法)
  • 非重名方法
    • 抽象方法需重写
    • 非抽象方法无需重写

实现类继承的父类与实现的接口间存在:

  • 重名方法
    • 抽象方法与父类方法重名,无需重写
    • 默认方法与父类方法重名,建议重写(不重写不会报错,默认用父类的,如想要使用接口的默认方法,需重写,调用 接口名.super.(); )

抽象类和接口的应用选择

理论区别

1.抽象类可以有构造方法,接口中不能有构造方法。(抽象类的匿名内部子类是无法重载构造函数的,只能super父类的构造函数,因为没有类名所以无法创建与类名相同的构造函数并重载它)

2.抽象类中可以有普通成员变量,也可以有静态变量;接口中只有默认即为public static final类型的变量
3.一个类可以实现多个接口,但只能继承一个抽象类。
4.抽象类中抽象、普通方法权限修饰符默认不写视为default,接口中抽象、普通方法权限修饰符默认不写视为public;

应用区别

5.继承抽象类,表示继承这一体系内容,用于共性内容的抽取;实现接口,表示功能有所拓展(该功能不一定仅服务于这一个体系,也可能服务于其他体系);

5.抽象类中可以包含非抽象的普通方法/静态方法,接口中的所有方法必须都是抽象的,不能有非抽象的普通方法/静态方法。

	(1.8  1.9版本新特性,接口已经可以有默认、静态和私有方法了)

应用选择

功能拓展推荐使用接口(可多继承
属性变量的值需要灵活变化时,使用抽象类
推荐继承抽象类的同时实现接口

匿名内部类

会被 eclipse自动 编译 成.class的二进制字节码文件,文件命名为“所在类名$内部类序号.class”

应用场景:需要重写父类A的方法a时,使用过程繁琐

  • 尤其是抽象类、接口必须这种重写的
  • 尤其当需要子类对象并调用方法,但不需要该对象使用第二次的
  • 在线程、IO流文件写入写出、网络编程场景常用(因为线程的开启和关闭只需要执行一次)

子类继承方式

创建子类继承抽象类,重写抽象方法,创建子类对象,调用重写后的方法

public abstract class A {
	int age=0;
	abstract void a();
	abstract void a1();
	public int a2(){
		return 2;
	}
}
	public class B extends A {
		public void a() {
					System.out.println("子类重写的a方法"+age);
				}
		public void a1() {
					System.out.println("子类重写的a1方法"+age);
				}
	}
	public class c {
		public staic void main(String[] args) {
				B b=new b;
				b.age=22;
				b.a();//子类重写的a方法
				b.a1();//子类重写的a1方法
				System.out.println(b.a2());//返回2
				}
	}

匿名内部类方式

原始方式
直接在类内部new一个父类对象,并重写抽象方法,在对象后直接调用方法;

	public class InnerClass {
		public static void main(String[] args) {
			new A(){
				@Override
				void a() {
					System.out.println("内部重写的a方法"+age);
				}
				@Override
				void a1() {
					System.out.println("内部重写的a1方法"+age);
				}
			}.a();//内部重写的a方法0
			//弊端1:写一个内部匿名类对象,只能调一次a或者a1或者a2;
			//弊端2:输出的age值永远为0,无法赋值,要赋值需要调用属性,调用属性就无法调用方法
		}
	}

改良方式
多态方式,即找一个父类引用变量aa指向子类匿名对象new A()(这个子类没有类名,所以也只能以多态方式找父类引用变量接收)

	public class InnerClass {
		public static void main(String[] args) {
			A aa = new A(){
				@Override
				void a() {
					System.out.println("内部重写的a方法"+age);
				}
				@Override
				void a1() {
					System.out.println("内部重写的a1方法"+age);
				}
			};
			aa.age=33;
			aa.a();//内部重写的a方法33
			aa.a1();//内部重写的a方法33
			//找一个A类的变量来接收A类的子类对象,照样内部重写
			System.out.println(aa.a2);//2
		}
	}

final最终的

修饰变量修饰对象修饰类修饰方法
可以
必须初始化
不能再被赋值
不可以再指向新的地址
但对象的属性可以变化
不能被继承
不能修饰接口、抽象类
可以修饰静态方法
不能修饰abstract 方法;
不能被继承重写
	相关词:
	--------------------------------------------
	final 关键字,不可变的
	finally 异常处理,try catch finally,finally后的代码必定被执行
	finalize 垃圾回收GC执行的最后一个功能
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值