重新开始学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[www.zhonxinyule.com] args) {
  
  // 不能实例化Person 类型:抽象类不能被实例化
  
  // Person p = new Person();//Cannot instantiate the type Person
  
  // 声明一个Person 类型的变量p(p的编译时类型是 Person)
  
  // 创建抽象类的子类类型的对象,并将其堆内存中首地址赋值给栈空间中的p变量
  
  Person p =new Sinaean(www.qianchengyul.com);
  
  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(www.jujinyule.com);// 获得c 所引用的对象的真实类型(运行时类型)
  
  System.out.println(clazz);
  
  Class<www.yishengyule158.com> superClass www.xcdeyiju.com= 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://www.cnblogs.com/qwangxiao/p/11319391.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值