重新开始学Java——抽象类、接口、内部类

抽象类

为什么要定义抽象方法:如果定义某个方法时不能确定该方法的具体实现细节; 比如定义 Person 类的 eat 方法时, 不能确定其具体实现细节,因为中国人、 西方国家的人、 南亚国家的人吃饭方式不一样。 可以把该方法定义成一个抽象方法,具体 的实现细节,交给其后代(子类)来实现。

抽象方法的定义

使用 abstract 关键字修饰方法的定义,方法体必须为空(否则就不是抽象方法),抽象方法必须是非静态的(抽象方法不能被 static 修饰), 抽象方法不能被 final 修饰、 不能被 private 修饰.

[ 修饰符 ] abstract 返回值类型 methodName() ; //注意这里没有 { }
抽象类的定义
为什么要定义抽象类:定义抽象方法的类, 必须被定义成抽象类

抽象类的定义方法

[ 修饰符 ] abstract class className {
	//使用 abstract 修饰类定义
}
抽象类的特征
  • 抽象类不能被实例化 (有构造但仅供子类调用)
  • 抽象类中可包含属性、 方法、 构造、 内部类、 枚举、 代码块等
  • 其中的方法可以是抽象方法, 也可以是已实现方法
  • 含有抽象方法的类, 必须被定义成抽象类
  • 这个抽象方法可能是自定义、 继承来的、 或实现自接口的,否则就要全部实现其中的抽象方法
  • 抽象类中, 可以没有抽象方法
DEMO
/**
* 为什么要有抽象类
* 1、含有抽象方法的类,必须被定义成抽象类;
* 但是,抽象类未必非要有抽象方法
* 2、如果期望当前类不能被实例化,
* 而是交给子类完成实例化操作,可以定义抽象类
* (抽象类有构造方法,抽象类不能被实例化(直接创建该类的对象))
* Person p = new Person(); // 错误的
*/
public abstract class Person {
	// 只要有花括号,就可以执行,这样的方法已经实现过了
	/**当一个方法在定义时无法确定其实现细节(具体处理什么、怎么处理)
	* 如果一个方法不是native 修饰的,
	* 当它没有方法体时,这个方法是不能被执行的,此时这个方法就是一个抽象的方法,
	* 需要使用抽象关键字来修饰(abstract)
	*/
	public abstract void eat(String food);
	/**
	* abstract 修饰方法时,不能与 static 、 final连用
	*/
}
/**
* 子类继承某个抽象类
* 1、如果子类依然不确定某个方法的实现细节(不实现继承自父类的抽象方法),
* 则可以将该类继续声明为抽象类
* 2、如果子类不希望是抽象类,必须实现继承自父类的抽象方法
*/
public class Sinaean extends Person{
	public Sinaean(){
		super();
	}
	// 这个方法不再是抽象方法了,而且可以有方法体
	@Override
	public void eat(String food) {
		System.out.println("中国人大部分都用筷子吃"+ food);
	}
}
public class Thai extends Person{
	@Override
	public void eat(String food) {
		System.out.println("泰国人有时候用手抓着吃: "+ food);
	}
}

/**
* 创建抽象类的实例:
* 1、创建其子类类型的实例(这是本质)
* 2、可以通过静态方法来获得其实例(本质还是创建子类类型对象 )
*/
public class Main {
	public static void main(String[] args) {
		// 不能实例化Person 类型:抽象类不能被实例化
		// Person p = new Person();//Cannot instantiate the type Person
		// 声明一个Person 类型的变量p(p的编译时类型是 Person)
		// 创建抽象类的子类类型的对象,并将其堆内存中首地址赋值给栈空间中的p变量
		Person p =new Sinaean();
		p.eat("火锅");
		System.out.println("运行时类型: " + p.getClass());System.out.println("内存中的首地址是: "+ System.identityHashCode(p));
		// 创建抽象类的子类类型的对象,并将其堆内存中首地址赋值给栈空间中的p变量
		// p 变量中原来存储的地址将被覆盖
		p = new Thai();
		p.eat("米饭");
		System.out.println("运行时类型: " + p.getClass());
		System.out.println("内存中的首地址是: "+ System.identityHashCode(p));
		Calendar c = Calendar.getInstance(); // 通过静态方法来获得一个实例
		Class<?> clazz = c.getClass();// 获得c 所引用的对象的真实类型(运行时类型)
		System.out.println(clazz);
		Class<?> superClass = clazz.getSuperclass();// 获得clazz的父类
		System.out.println(superClass);
	}
}
总结
抽象类的特点
1、抽象类【有构造】,但是不能被实例化
	抽象类的构造方法专供子类调用(构造方法也是可以执行的)
2、抽象类中可以有抽象方法,也可以没有
	含有抽象方法的类必须是抽象类(参看Person中的第一点)
	抽象类中可以没有抽象方法(参看Person中的第二点)
3、怎么创建抽象类的实例:创建其子类类型的实例(这是本质)
	待创建对象的子类类型必须是非抽象类
	不一定非要是直接子类,间接子类也可以
	可以通过静态方法来获得其实例(Calendar.getInstance() )
	Calendar c = Calendar.getInstance();
4、应该选择哪种方式来创建抽象类的实例:
a>、如果当前抽象类中有静态方法,则优先使用静态方法
b>、如果子类中有静态方法返回相应实例,用子类的静态方法
c>、寻找非抽象的子类,创建子类类型的对象即可
d>、自己继承这个类并实现其中的抽象方法,然后创建实例
注意:有时为了实现我们的需求,可能会不调用静态方法来获得实例,而是选择创建子类对象

接口

接口是一种比抽象类更抽象的类型;接口是从多个相似的类中抽象出来的规范: 它定 义了某一批类(接口的实现类或实现类的子类)所要必须遵循的规范。 接口只定义常量 或方法, 而不关注方法的实现细节,接口体现了规范和实现相分离的设计哲学。

定义接口
[ 修饰符 ] interface InterfaceName {
	定义在接口中的常量 ( 0 到 n 个)
	定义在接口中的抽象方法 ( 0 到 n 个)
	static 修饰的方法 ( 0 到 n 个)
	default 修饰的方法( 0 到 n 个)
}
接口中的成员
  • 可以包含属性(只能是常量)
  • 系统会对没有显式使用 public static final 修饰的变量追加这些修饰
  • 可以包含方法(必须是抽象的实例方法)
  • 接口中不允许有非抽象的方法
  • 接口中可以存在静态方法( 用 static 修饰的静态方法)
  • 接口中可以存在默认方法( 用 default 修饰的方法 )
  • 可以包含内部类或内部接口
  • 可以包含枚举类
  • 不能包含构造方法
  • 不能包含代码块
接口的继承和实现

接口的继承使用 extends 关键字实现:

public interface Usb1 extends Usb {};

Java 语言中的接口可以继承多个接口: 多个接口之间使用 , 隔开 ( 英文状态的逗 号 );子接口可以继承父接口中的: 抽象方法、常量属性、内部类、枚举类

类可以实现接口:使用 implements 关键字来实现

接口的特征

接口中的属性默认都是 public 、 static 、 final 类型:这些成员必须被显式初始化;接口中的方法默认都是 public 、 abstract 类型的。

接口中根本就没有构造方法, 也就可能通过构造来实例化,但允许定义接口类型的引用变量,该引用变量引用实现了这个接口的类的实例。

接口不能实现另一个接口, 但可以继承多个接口。

接口必须通过实现类来实现它的抽象方法,当某个类实现了某个接口时, 必须实现其中所有的抽象方法,或者是不实现其中的抽象方法, 而把该类定义成抽象类。

类只能继承一个类, 但可以实现多个接口,多个接口之间用逗号分开。

接口和抽象类的异同
共同点:
	接口和抽象类都不能被实例化
	接口和抽象类都处于继承树的顶端
	接口和抽象类都可以包含抽象方法
	实现接口或继承抽象类的普通类必须实现其中的抽象方法
区别
	抽象类中可以有非抽象方法, 接口中只能有抽象方法或static修饰的方法或default修饰的方法
	一个类只能继承一个直接父类, 而接口可以实现多继承
	抽象类可定义静态属性和普通属性, 而接口只能定义静态属性
	抽象类有自己的构造, 接口完全没有
	抽象类中可以有代码块, 接口中不可以有
DEMO
/**
* 声明接口,并确定接口中可以有什么
* 1、常量
* 2、抽象方法
* 3、 default 修饰的非抽象方法(JDK1.8开始)* 4、接口没有构造方法
*/
public interface Usb {
	// 接口没有构造方法
	// public Usb(){}
	/**
	* 接口中只能定义常量(没有不是不是常量的属性)
	* 1、接口中所有的属性默认都是 public static final 修饰的
	* 2、常量的命名:所有字母都是大写,如果有多个单词,中间用下划线隔开
	* */
	int POWER_UNIT = 100 ;// 充当供电单位
	/**
	* JDK1.8 之前 仅允许在接口中声明抽象方法
	* 所有的方法都是 public abstrct 修饰的
	*/
	void power();
	/**
	* JDK1.8 开始,允许定义被default修饰的非抽象方法
	* 这个方法是个public 修饰的非静态方法(子类或子接口可以重写)
	*/
	default void show(){
		System.out.println("每次供电单位是: "+POWER_UNIT) ;
	}
}

/**
* 1、类 可以实现接口,用关键字implements来完成实现
* 2、如果本来不希望是抽象类,则需要实现从接口"继承"的所有抽象方法
*/
public class MiUsb extends Object implements Usb {
	/**
	* MiUsb中都有什么
	* 从Object中继承的所有方法
	* 从Usb中继承的常量
	* 从Usb中继承的default的方法(JDK1.8开始)
	* 实现了所有的抽象方法
	*/
	@Override
	public void power() {
		System.out.println("小米Usb充电器,供电单位: "+POWER_UNIT);
	}
	@Override
	public void show() {
		Usb.super.show();
	}
}

public class Test {
	public static void main(String[] args) {
		// 声明一个接口类型的引用变量Usb u = null;
		// 创建实现类的实例 并将其堆内存首地址赋值给u变量
		u = new MiUsb();
		u.power();
	}
}

一个类实现多个接口 

public interface Transfer {
	void transmission();
}

/**
* 1、用接口继承接口
* 2、接口可以继承父接口中的常量、抽象方法、 default方法
* 3、一个接口可以继承多个接口,中间用逗号隔开就行
*/
public interface UsbTypeC extends Usb , Transfer{}

/**
* 一个类可以实现多个接口,中间用逗号分隔开就可以
*/
public class OppoUsb implements UsbTypeC,Usb{
	@Override
	public void transmission() {
		System.out.println("Oppo手机");
	}
	@Override
	public void power() {
		System.out.println("Oppo 手机,供电单位"+ POWER_UNIT);
	}
}

public class Test2 {
	public static void main(String[] args) {
		// 声明一个接口类型的引用变量
		OppoUsb u = null;
		u = new OppoUsb();
		u.power();// 实现了Usb接口中的方法
		u.transmission();// 实现了Transfer接口中的方法
	}
}

内部类

内部类的分类如下:

成员内部类:
	实例内部类
	静态内部类
局部内部类:
	匿名内部类
DEMO
public class Human {/* 类体括号 */
	public static void main(String[] args){ // main 方法的方法体开始
		int a = 250;
		System.out.println(a);
		class ON{ // 局部内部类(Local Inner Class)
		}
		ON oo = new ON();
		System.out.println(oo);
		class OFF{ // 局部内部类(Local Inner Class)
		}
	}// main 方法的方法体结束static String earth; // 属性:静态属性( 类属性 )
	
	String name ; // 属性 :实例属性(实例变量)
	static class Country{ // 静态内部类[ static Inner Class]
	}
	class Head{// 实例内部类 (成员内部类) [ Member Inner Class ]
	}
	class Hand{// 实例内部类
	}
}
获得到自己的内部类
/**
* 获得某个类内部的所有的静态内部类和所有的成员内部类
* 注意:不能获得到局部内部类
*/
public class GetInnerClass {
	public static void main(String[] args) {
		Class<?> c = Human.class;
		// 获得 c 内部的内部类(静态内部类、成员内部类)
		Class<?>[] classes = c.getDeclaredClasses();// 获得本类内声明的非 局部内部类
		for (int i = 0; i < classes.length; i++) {
			Class<?> cc = classes[i];
			System.out.println(cc);
		}
	}
}

也可以通过一个内部类获取自己声明在哪个类内部

public class GetOutterClass {
	public static void main(String[] args) {
		Class<?> c = Human.Country.class;
		// 获得某个内部类声明在那个外部类中
		Class<?> oc = c.getDeclaringClass(); // 获得声明自己的 那个类
		System.out.println(oc);
	}
}
创建静态内部类的实例
public class GetInstance1 {
	public static void main(String[] args) {
		/** 静态内部类的实例 */
		Human.Country c = new Human.Country();
		System.out.println(c);
		/** 实例内部类的实例 */
		Human h = new Human();// 创建外部类的实例
		Human.Hand hand = h.new Hand();// 以外部类的实例 h 为基础,创建内部类的实例
		System.out.println(hand);
		// 或者:
		Human.Head head= new Human().new Head();
		System.out.println(head);
	}
}

匿名内部类

有一个局部内部类,它连名字都没有,则它就是匿名内部类,但是它有对应的.class文件。

新建一个新的 Class,叫做 TestAnonyous1。随后创建一个接口,叫做 USB,并提供一个方法(void transfer) 。具体在 TestAnonyous1 中的例子:用匿 名内部类实现接口。

DEMO
/**
* 创建匿名内部类
*/
public class TestAnonymours1 {
	public static void main(String[] args) {
		// 编译时类型:变量u 声明的类型是USB
		// 用匿名内部类来实现接口
		USB u = new USB(){
		@Override
		public void transfer() {
			System.out.println("USB正在传输");
		}
		};// 把USB当成尸体,结果鬼{}上身了,就能实例化了
		u.transfer();
		System.out.println(System.identityHashCode(u));
		// 获得创建的实例的运行时类型
		Class<?> c = u.getClass();// 任何一个对象都可以通过getClass来获得其 运行时类型
		System.out.println(c);
		Class<?> oc = c.getDeclaringClass();// 尝试获得声明自己的那个外部类
		System.out.println(oc);// null 说明 匿名内部类不是直接声明在类体内部的
		Class<?>[] inters = c.getInterfaces();
		for (int i = 0; i < inters.length; i++) {
			System.out.println(inters[i]);
		}
	}
}
public abstract class AbstractUSB implements USB{
// 从实现的接口中继承了抽象方法 transfer
}
/**
* 创建匿名内部类
*/
public class TestAnonymours2 {
	public static void main(String[] args) {
	// 创建一个抽象类的实例(本质一定是创建其子类类型的实例)
	// 用匿名内部类来继承抽象类,并实现其中的抽象方法
	AbstractUSB au = new AbstractUSB() {
		@Overridepublic void transfer() {
			System.out.println("AbstractUSB正在传输");
		}
	};
	au.transfer();
	Class<?> c = au.getClass();// 获得au 对象的运行时类型
	System.out.println("匿名内部类: "+ c.getName());
	Class<?> sc = c.getSuperclass();
	System.out.println("匿名内部类的父类: " + sc.getName() );
	}
}
/**
* 创建匿名内部类
*/
public class TestAnonymours3 {
	public static void main(String[] args) {
	// 用匿名内部类继承一个普通的类
	// 并重写其中的方法
	Object o = new Object(){
		@Override
		public String toString(){
		return "我是鬼。。 ";
	}};
	System.out.println(o);
	System.out.println(o.getClass());
	System.out.println(o.getClass().getSuperclass());
	}
}
总结
1、内部类
	嵌套在另一个类内部的类
2、内部类的分类
	直接写在类体括号内的:
	静态内部类、非静态内部类(实例内部类、成员内部类)
	不是直接写在类体括号内,比如写在方法中、写在代码块中:局部内部类
	如果某个局部内部类连名字都没有,那它就是匿名内部类
3、问题:
	对于 Human.java 来说有一个与它对应的 Human.class 文件,内部类是否有对应的 .class 文件?
	有.class 文件,对于静态内部类、实例内部类来说,他们对应的 .class 的名称是:
		外部类类名$内部类类名.class比如 Human 类中的 Country 类对应的 字节码文件的名称是:Human$Country.class
	对于局部内部类(有名称的)来说:他们对应的 .class 文件名称是:外部类类名$Number 内部类类名.class
		其中的 Number 是 使用 该名称 的 内部类 在 外部类 出现的位置(第几个)
		
4、一个类能否获取到自己的内部类(静态内部类、成员内部类)
	GetInnerClass.java
	
5、一个内部类能否获取自己声明在哪个类内部: GetOutterClass.java

6、创建内部类的实例
	a>、局部内部类的实例,只能在当前的代码块内部使用,
	比如 Human 类内部的 main 方法的 ON 类,则这个类只能在 main 方法内部使用
		class ON{ // 局部内部类(Local Inner Class)
		}
		ON oo = new ON();// 创建局部内部类的实例
		System.out.println(oo);
	b>、 创建静态内部类的实例:
		// 外部类.静态内部类 变量名 = new 外部类.静态内部类()Human.Country c = new Human.Country();
	c>、创建实例内部类的实例:
		Human h = new Human();// 创建外部类的实例
		外部类.实例内部类 变量名 = 外部类实例.new 实例内部类();
		Human.Hand hand = h .new Hand();
7、匿名内部类
	有一个局部内部类,它连名字都没有,则它就是匿名内部类,但是它有对应的.class 文件
	匿名内部类对应的.class 文件名 是外部类类名$数字.class
	a>、用匿名内部类实现接口: TestAnonymous1.java
	b>、用匿名内部类继承抽象类: TestAnonymous2.java
	c>、用匿名内部类继承普通的类: TestAnonymous3.java

转载于:https://my.oschina.net/lujiapeng/blog/3084963

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值