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

抽象类

为何要定义抽象方法:若是定义某个方法时不能肯定该方法的具体实现细节; 好比定义 Person 类的 eat 方法时, 不能肯定其具体实现细节,由于中国人、 西方国家的人、 南亚国家的人吃饭方式不同。 能够把该方法定义成一个抽象方法,具体 的实现细节,交给其后代(子类)来实现。java

抽象方法的定义

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

[ 修饰符 ] abstract 返回值类型 methodName() ; //注意这里没有 { }

抽象类的定义

为何要定义抽象类:定义抽象方法的类, 必须被定义成抽象类

抽象类的定义方法设计

[ 修饰符 ] abstract class className {
	//使用 abstract 修饰类定义
}

抽象类的特征

  • 抽象类不能被实例化 (有构造但仅供子类调用)
  • 抽象类中可包含属性、 方法、 构造、 内部类、 枚举、 代码块等
  • 其中的方法能够是抽象方法, 也能够是已实现方法
  • 含有抽象方法的类, 必须被定义成抽象类
  • 这个抽象方法多是自定义、 继承来的、 或实现自接口的,不然就要所有实现其中的抽象方法
  • 抽象类中, 能够没有抽象方法

DEMO

/**
* 为何要有抽象类
* 一、含有抽象方法的类,必须被定义成抽象类;
* 可是,抽象类未必非要有抽象方法
* 二、若是指望当前类不能被实例化,
* 而是交给子类完成实例化操做,能够定义抽象类
* (抽象类有构造方法,抽象类不能被实例化(直接建立该类的对象))
* Person p = new Person(); // 错误的
*/
public abstract class Person {
	// 只要有花括号,就能够执行,这样的方法已经实现过了
	/**当一个方法在定义时没法肯定其实现细节(具体处理什么、怎么处理)
	* 若是一个方法不是native 修饰的,
	* 当它没有方法体时,这个方法是不能被执行的,此时这个方法就是一个抽象的方法,
	* 须要使用抽象关键字来修饰(abstract)
	*/
	public abstract void eat(String food);
	/**
	* abstract 修饰方法时,不能与 static 、 final连用
	*/
}
/**
* 子类继承某个抽象类
* 一、若是子类依然不肯定某个方法的实现细节(不实现继承自父类的抽象方法),
* 则能够将该类继续声明为抽象类
* 二、若是子类不但愿是抽象类,必须实现继承自父类的抽象方法
*/
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);
	}
}

/**
* 建立抽象类的实例:
* 一、建立其子类类型的实例(这是本质)
* 二、能够经过静态方法来得到其实例(本质仍是建立子类类型对象 )
*/
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);
	}
}

总结

抽象类的特色
一、抽象类【有构造】,可是不能被实例化
	抽象类的构造方法专供子类调用(构造方法也是能够执行的)
二、抽象类中能够有抽象方法,也能够没有
	含有抽象方法的类必须是抽象类(参看Person中的第一点)
	抽象类中能够没有抽象方法(参看Person中的第二点)
三、怎么建立抽象类的实例:建立其子类类型的实例(这是本质)
	待建立对象的子类类型必须是非抽象类
	不必定非要是直接子类,间接子类也能够
	能够经过静态方法来得到其实例(Calendar.getInstance() )
	Calendar c = Calendar.getInstance();
四、应该选择哪一种方式来建立抽象类的实例:
a>、若是当前抽象类中有静态方法,则优先使用静态方法
b>、若是子类中有静态方法返回相应实例,用子类的静态方法
c>、寻找非抽象的子类,建立子类类型的对象便可
d>、本身继承这个类并实现其中的抽象方法,而后建立实例
注意:有时为了实现咱们的需求,可能会不调用静态方法来得到实例,而是选择建立子类对象

接口

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

定义接口

[ 修饰符 ] 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 类型的。内存

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

接口不能实现另外一个接口, 但能够继承多个接口。it

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

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

接口和抽象类的异同

共同点:
	接口和抽象类都不能被实例化
	接口和抽象类都处于继承树的顶端
	接口和抽象类均可以包含抽象方法
	实现接口或继承抽象类的普通类必须实现其中的抽象方法
区别
	抽象类中能够有非抽象方法, 接口中只能有抽象方法或static修饰的方法或default修饰的方法
	一个类只能继承一个直接父类, 而接口能够实现多继承
	抽象类可定义静态属性和普通属性, 而接口只能定义静态属性
	抽象类有本身的构造, 接口彻底没有
	抽象类中能够有代码块, 接口中不能够有

DEMO

/**
* 声明接口,并肯定接口中能够有什么
* 一、常量
* 二、抽象方法
* 三、 default 修饰的非抽象方法(JDK1.8开始)* 四、接口没有构造方法
*/
public interface Usb {
	// 接口没有构造方法
	// public Usb(){}
	/**
	* 接口中只能定义常量(没有不是否是常量的属性)
	* 一、接口中全部的属性默认都是 public static final 修饰的
	* 二、常量的命名:全部字母都是大写,若是有多个单词,中间用下划线隔开
	* */
	int POWER_UNIT = 100 ;// 充当供电单位
	/**
	* JDK1.8 以前 仅容许在接口中声明抽象方法
	* 全部的方法都是 public abstrct 修饰的
	*/
	void power();
	/**
	* JDK1.8 开始,容许定义被default修饰的非抽象方法
	* 这个方法是个public 修饰的非静态方法(子类或子接口能够重写)
	*/
	default void show(){
		System.out.println("每次供电单位是: "+POWER_UNIT) ;
	}
}

/**
* 一、类 能够实现接口,用关键字implements来完成实现
* 二、若是原本不但愿是抽象类,则须要实现从接口"继承"的全部抽象方法
*/
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();
}

/**
* 一、用接口继承接口
* 二、接口能够继承父接口中的常量、抽象方法、 default方法
* 三、一个接口能够继承多个接口,中间用逗号隔开就行
*/
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());
	}
}

总结

一、内部类
	嵌套在另外一个类内部的类
二、内部类的分类
	直接写在类体括号内的:
	静态内部类、非静态内部类(实例内部类、成员内部类)
	不是直接写在类体括号内,好比写在方法中、写在代码块中:局部内部类
	若是某个局部内部类连名字都没有,那它就是匿名内部类
三、问题:
	对于 Human.java 来讲有一个与它对应的 Human.class 文件,内部类是否有对应的 .class 文件?
	有.class 文件,对于静态内部类、实例内部类来讲,他们对应的 .class 的名称是:
		外部类类名$内部类类名.class好比 Human 类中的 Country 类对应的 字节码文件的名称是:Human$Country.class
	对于局部内部类(有名称的)来讲:他们对应的 .class 文件名称是:外部类类名$Number 内部类类名.class
		其中的 Number 是 使用 该名称 的 内部类 在 外部类 出现的位置(第几个)
		
四、一个类可否获取到本身的内部类(静态内部类、成员内部类)
	GetInnerClass.java
	
五、一个内部类可否获取本身声明在哪一个类内部: GetOutterClass.java

六、建立内部类的实例
	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();
七、匿名内部类
	有一个局部内部类,它连名字都没有,则它就是匿名内部类,可是它有对应的.class 文件
	匿名内部类对应的.class 文件名 是外部类类名$数字.class
	a>、用匿名内部类实现接口: TestAnonymous1.java
	b>、用匿名内部类继承抽象类: TestAnonymous2.java
	c>、用匿名内部类继承普通的类: TestAnonymous3.java
相关文章
相关标签/搜索