欢迎访问悦橙教程(wld5.com),关注java教程。悦橙教程  java问答|  每日更新
页面导航 : > > > 文章正文

JavaSE 10 面向对象的高级特性,javase面向对象

来源: javaer 分享于  点击 42253 次 点评:27

JavaSE 10 面向对象的高级特性,javase面向对象


1、抽象

⑴ 概念


往往将具有抽象行为的父类设计成抽象类。不需要创建该类的对象,里面的某些行为不好描述。

⑵ abstract关键字


可以修饰类和方法,但不能修饰属性、局部变量、构造器和初始化块。

⑶ 特点

① 抽象类的特点

⒈ 抽象类中可以有普通的成员,包括普通属性和普通方法。
⒉ 抽象类不能创建本类的对象。
⒊ 抽象类有构造器(所有的类都有构造器)。抽象类的构造器的作用是为了被子类调用,而不是为了创建本类对象。
⒋ 抽象类不可以被private、static、final修饰。
⒌ 抽象类中可以没有抽象方法,但是很少这样做。
⒍ 子类继承抽象父类,必须重写抽象父类中的抽象方法。但是抽象子类继承抽象父类,则不需要重写抽象父类中的抽象方法。
⒎ 抽象类的本质,就是为了派生子类。

② 抽象方法的特点


⒈ 抽象方法不能放在实现类中,它只能放在抽象类中。
⒉ 抽象方法不能用private、static、final修饰,因为这会和方法的重写相冲突。
⒊ 抽象方法只能有方法的签名,不能有方法体,并用分号结尾。
⒋ 抽象方法的本质,就是为了让子类重写。

2、模版方法设计模式

模版父类的功能:⑴ 已经提供具体实现的方法,可以让子类继承并使用;⑵ 抽象方法,可以让子类重写。
好处就是提高代码的重用性。

示例:查看某段代码的执行总耗时

public class Test {
  public static void main(String[] args) {
    CodeClass cc = new CodeClass();
    cc.calcTime();
  }
}

abstract class CalcTime {
  protected final void calcTime() { // 不需要子类重写,只需子类继承
    long begin = System.currentTimeMillis(); // 计算代码执行前的时间

    code(); // 执行抽象方法【需要子类实现】

    long end = System.currentTimeMillis(); // 计算代码执行后的时间
    System.out.println("总耗时:" + (end - begin) + "ms"); // 计算总耗时
  }

  protected abstract void code();
}

class CodeClass extends CalcTime {
  @Override
  protected void code() { // 子类重写抽象方法【需要测试耗时的代码】
    String str = "Hi";
    for (int i = 0; i < 5000; i++) {
      str += "Java";
    }
  }
}

3、接口

⑴ 概念

接口就是一个特殊的“抽象类”,它比抽象类的层级更靠上,里面都是抽象方法。

接口                     全是抽象方法
抽象类                 一部分方法实现了,一部分方法没有实现
实现类                 所有的方法都实现了

⑵ 好处

① 它避免了Java单继承的局限性。
② 接口的使用更加地灵活,它从逻辑上来说,不像继承需要 …是… ;它只需 …像… 即可。
③ 它提高了解耦性,降低了类和类之间的依赖性。

⑶ 特点

① 定义特点


⒈ 使用interface关键字来定义。
⒉ 抽象类中没有普通成员,只有抽象方法和常量。
⒊ 接口中的方法的修饰符只能是public abstract,换成其它的会报错,可以省略;常量中的修饰符只能是public static final,换成其它的会报错,可以省略。
示例:
【抽象方法】public abstract void method(); 或 void method();
【常量】public static final String COUNTRY = “中国”; 或 String COUNTRY = “中国”;
⒋ 接口中不能创建本类的对象。
⒌ 接口没有构造器(接口不属于类)。
⒍ 接口只能用public 或 默认访问修饰符修饰。

② 扩展特点


⒈ 通过implements关键字来实现。
⒉ 一个类可以实现多个接口,每个接口之间用逗号(,)隔开。
示例:class 类名 implements 接口1, 接口2, … { }
⒊ 一个类可以同时继承其他类,又可以实现多个接口。
示例:class 类名 extends 父类名 implements 接口1, 接口2, … { }
⒋ 一个接口可以继承多个接口,每个接口之间用逗号(,)隔开。
示例:interface 接口名 extends 接口名1, 接口名2, … { }
⒌ 总结:㈠ 类和类之间是单继承;㈡ 接口和接口之间是多继承。㈢ 类和接口之间是多实现;

⑷ 应用

接口和父类,在应用上是一致的,既可以用在多态参数上,也可以用在多态数组上。

① 多态参数

方法:
    访问修饰符 返回值类型 方法名 (接口名 形参名){
          形参名.方法( );
    }

    调用:方法名(new 实现类( ));

② 多态数组

    接口名[] 数组名 = new 接口名[数组长度];
    数组名[下标] = new 实现类( );

⑸ 用法总结

① 通过接口可以实现不相关类的相同行为,而不需要考虑这些类之间的层次关系。
② 通过接口可以指明多个类需要实现的方法,一般用于定义对象的扩张功能。
③ 接口主要用来定义规范。解除耦合关系。

4、接口和抽象方法的对比

                      接口(interface)                   抽象类(abstract class)

有无构造器                                  没有                                                                    有
能否被实例化                              不能                                                                  不能
有无普通成员                              没有                                                                    有
抽象方法的修饰符       【只能是】public abstract                   【除了private、static、final外】abstract

相同点:⑴ 都含有抽象方法。
               ⑵ 都是为了派生子类。
               ⑶ 都可以应用在多态上。
               ⑷ 如果是抽象子类继承抽象父类,或抽象类实现接口,则抽象类(抽象子类)不需要实现父类或接口中的方法(抽象)。

5、接口和抽象类之间的关系

6、工厂方法(FactoryMethod)

⑴ 概念

定义一个用于创建对象的接口(工厂),让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
在面向对象的编程中,对象的创建工作简单,但是对象的创建时机却很重要。工厂方法可以将所要创建的具体对象的创建工作延迟到了子类,从而提供了一种扩展的策略,较好的解决了这种紧耦合的关系。

⑵ 示例

public class Test {
  public static void main(String[] args) {
    PhoneFactory pf = new MeizuFactory();
    pf.makePhone().tel(); // 用魅族手机打电话

    pf = new MiFactory();
    pf.makePhone().tel(); // 用小米手机打电话
  }
}

/**
 * 生产手机的厂商
*/
interface PhoneFactory {
  Phone makePhone(); // 生产手机的方法
}

/**
* 手机规范
*/
interface Phone {
  void tel(); // 每个手机都能打电话
}

/**
* 魅族厂商
*/
class MeizuFactory implements PhoneFactory {
  @Override
  public Phone makePhone() {
    return new Meizu();
  }
}

/**
* 魅族手机
*/
class Meizu implements Phone {
  @Override
  public void tel() {
    System.out.println("用魅族手机打电话");
  }
}

/**
* 小米厂商
*/
class MiFactory implements PhoneFactory {
  @Override
  public Phone makePhone() {
    return new Mi();
  }
}

/**
* 小米手机
*/
class Mi implements Phone {
  @Override
  public void tel() {
    System.out.println("用小米手机打电话");
  }
}

7、代理模式(Proxy)

⑴ 理解

多一个代理类出来,用以代替原对象进行一些操作。例如租房子找中介、打官司请律师等。

⑵ 示例

public class Test {
  public static void main(String[] args) {
   Apple apple = new Apple(); // 委托人
   Fushikang fushikang = new Fushikang(apple); // 代理人
   fushikang.makePhone(); // 苹果公司生产iPhone
 }
}

/**
* 生产手机的厂商
*/
interface PhoneFactory {
  void makePhone(); // 生产手机的方法
}

/**
* 苹果公司【委托人】
*/
class Apple implements PhoneFactory {
  @Override
  public void makePhone() {
    System.out.println("苹果公司生产iPhone");
  }
}

/**
* 富士康【代理人】
*/
class Fushikang implements PhoneFactory {
  PhoneFactory pf;

  public Fushikang(PhoneFactory pf) { // 需要通过构造器来创建委托人对象
    this.pf = pf;
  }

  @Override
  public void makePhone() {
    pf.makePhone(); // 调用委托人的方法
  }
}

8、内部类

⑴ 概念

一个类中又可以完整的嵌套另外一个类。前者类称为外部类,后者被嵌套的类称为内部类。其它的类称为外部其它类。
内部类隶属于外部类或某个方法或某个代码块,但是本质上它是一个类。
编译后的class文件名为:外部类名$内部类名.class

   class Outer { // 外部类
     class Inner { // 内部类
     }
   }
   class Other { // 外部其它类
   }

⑵ 分类

① 定义在类体中


其地位等同于其他类的成员(成员变量等)。

⒈ 没有用static修饰:成员内部类


成员内部类的特点:
㈠ 可以加访问修饰符。
㈡ 可以有属性,方法,构造器,初始化块和内部类。
㈢ 可以直接访问外部类的私有成员。
㈣ 当外部类的属性名和内部类的属性名重名时,默认会访问内部类的属性,遵循就近原则。如果想访问外部类的同名属性,则需用外部类名.this.属性名 来调用
㈤ 不允许定义静态成员(包括静态属性、静态方法、静态初始化块和静态内部类)。这是因为类的加载顺序为:加载外部类 > 加载静态成员(外部类的和内部类的) > 加载内部类。
㈥ 互访原则:1) 内部类可以直接访问外部类的所有成员。2) 外部类或外部其它类可以通过创建内部类的对象,来调用内部类的成员。

外部类创建内部类的对象的语法为:内部类名 名 = new 内部类名( );
外部其它类创建内部类的对象的语法为:外部类名.内部类名 名 = new 外部类名( ).new 内部类名( );
示例:

public class Test {
  public static void main(String[] args) {
    Outer.Inner oi = new Outer().new Inner(); // 外部其它类创建内部类的对象
    oi.method();

    Outer outer = new Outer();
    outer.function();
  }
}

class Outer {
  class Inner {
    private String name = "张三";

    public void method() {
      System.out.println(name);
    }
  }

  void function() {
    Inner inner = new Inner(); // 外部类创建内部类的成员
    inner.method();
  }
}

⒉ 使用static修饰:静态内部类


静态内部类的特点:
㈠ 可以加访问修饰符。
㈡ 可以有属性,方法,构造器,初始化块和内部类。
㈢ 可以直接访问外部类的私有成员。
㈣ 当外部类的属性名和内部类的属性名重名时,默认会访问内部类的属性,遵循就近原则。如果想访问外部类的同名属性,则需用外部类名.this.属性名 来调用
㈤ 静态内部类可以定义静态成员(包括静态属性、静态方法、静态初始化块和静态内部类)。
㈥ 互访原则:1) 内部类可以直接访问外部类的所有成员。2) 外部类或外部其它类可以通过创建内部类的对象,来调用内部类的普通或静态成员;也可以通过内部类.静态成员名 的方式,来调用内部类的静态成员

外部类创建内部类的对象的语法为:内部类名 名 = new 内部类名( );
外部其它类创建内部类的对象的语法为:外部类名.内部类名 名 = new 外部类名( ).new 内部类名( );
通过内部类名.成员的方式来调用内部类的静态成员的语法:内部类名.静态成员名;
示例:

public class Test {
  public static void main(String[] args) {
    Outer.Inner.method();

    Outer outer = new Outer();
    outer.function();
  }
}

class Outer {
  static class Inner {
    private static String name = "张三";

    public static void method() {
      System.out.println(name);
    }
  }

  void function() {
    System.out.println(Inner.name); // 通过类名.属性名来调用 name
  }
}

② 定义在方法或代码块中


其地位等同于方法中的局部变量。

⒊ 有类名的:局部内部类


局部内部类的特点:
㈠ 不能加访问修饰符(可以联想到局部变量不能被访问修饰符修饰)。最根本的原因就是局部变量的访问就是被限制在方法体或代码块中,用不着访问修饰符。
㈡ 可以有属性、方法、构造器、初始化块和内部类。
㈢ 可以直接访问外部类中的成员。
㈣ 不能定义静态成员(可以联想到局部变量不能被static修饰)。最根本的原因还是由于类的加载顺序,所有的静态成员需要在创建对象前,就都被加载到内存中【此时对象都没有,方法怎么可能被调用】。
㈤ 局部内部类的作用范围仅仅作用在定义它的方法或代码块中。而其他地方,例如其他方法或其他类中都不可以访问。
㈥ 当外部类的属性名和内部类的属性名重名时,默认会访问内部类的属性,遵循就近原则。如果想访问外部类的同名属性,则需用外部类名.this.属性名 来调用
因为局部变量的声明周期要小于局部内部类的声明周期,所以局部内部类只能访问用final修饰的局部变量。
示例:

public class Test {
  public static void main(String[] args) {
    new Outer().method();
  }
}

class Outer {
  String name = "张三";

  void method() {
    final String name = "李四";

    class Inner {
      void function() {
        System.out.println(name); // 【就近原则,会访问局部变量(李四),如果不加final,则会报错Cannot refer to the non-final local variable name defined in an enclosing scope
      }
    }

    new Inner().function();

    System.out.println(Outer.this.name); // 通过外部类名.this.属性名,来访问外部类的成员变量(张三)
  }
}

⒋ 没有类名的:匿名内部类


一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类。

    语法:
      new 父类构造器(实参列表) | 实现接口( ){
        // 匿名内部类的类体
      };

    对比:
      new 类名( ); 创建某类的对象
      new 类名( ){ }; 创建某类的子类或对象

匿名内部类的特点:
㈠ 不能加访问修饰符。
㈡ 匿名内部类中的成员不包括构造器。
㈢ 可以直接访问外部类的所有成员。
㈣ 当外部类的属性名和内部类的属性名重名时,默认会访问内部类的属性,遵循就近原则。如果想访问外部类的同名属性,则需用外部类名.this.属性名 来调用
㈤ 匿名内部类的作用范围仅仅作用在定义它的方法或代码块中。而其他地方,例如其他方法或其他类中都不可以访问。
因为局部变量的声明周期要小于匿名内部类的声明周期,所以匿名内部类只能访问用final修饰的局部变量。
㈦ 匿名内部类的应用:一般用在方法的传参,可以简化代码。

示例:

public class Test {
  public static void main(String[] args) {
    new Outer().method();

    Test t = new Test();
    t.getSeed(new Flower(){ // 这里传入一个匿名内部类
       @Override
       public void makeSeed() {
         System.out.println("得到向日葵的种子");
       }
    });

  }

  private void getSeed(Flower f) {
   f.makeSeed();
  }
}

interface Flower {
  void makeSeed();
}

abstract class Person {
  public abstract void show();
}

class Outer {

  void method() {
    final String name = "张三"; // 匿名内部类只能访问用final修饰的局部变量

    new Person(){
      @Override
      public void show() {
        System.out.println(name);
      }
    }.show(); // 通过匿名内部类,来调用类中的show 方法 【类似于 new Person().show(); 不同于普通的实现类,因为Person是一个抽象类,里面需要重写抽象方法】

  }

}

9、枚举

⑴ 特点


在枚举类中,定义一组限定的值。例如一年有四季等。其类似于单例模式(类中只有一个值),而枚举则是有一组值。

⑵ JDK5.0之前 自定义枚举类

步骤:① 私有化构造器
② 创建一组公共的、静态的对象
示例:

class Season {
  private Season(){ } // 私有化构造器

  // 一组公共的、静态的对象
  public static final Season SPRING = new Season();
  public static final Season SUMMER = new Season();
  public static final Season AUTUMN = new Season();
  public static final Season WINTER = new Season();
}

⑶ JDK5.0之后 使用关键字定义枚举类

语法:① 使用enum关键字,来定义枚举类
           ② 对象必须放在首行,每个对象之间用逗号(,)隔开,最后用分号结尾(;)
语法为: 对象名(实参列表), 对象名(实参列表), … 对象名(实参列表);

示例:

enum Season {
  SPRING(), SUMMER(), AUTUMN(), WINTER();
}

特点:① 系统默认提供私有的无参构造器
           ② 还可以定义其他的属性和方法
           ③ 使用enum关键字定义的枚举类都默认继承了Enum类。

⑷ Enum类的相关方法

toString( )方法


重写了Object类中的toString方法,它返回枚举常量的名称。即定义时的对象名称。

name( )方法


和toString方法的返回值相同,都是返回此枚举常量的名称。不过一般使用toString较多。

valueOf(String name)方法


这是一个静态方法。
将字符串转换为枚举对象,要求字符串必须为枚举类中定义过的枚举常量的名称之一。如果不存在,会报错:java.lang.IllegalArgumentException

values( )方法


这是一个静态方法。
返回当前枚举类中所有的定义过的对象。

ordinal( )方法


返回当前枚举类对象的位置号,位置号从0开始。

示例:

public class Test {
  public static void main(String[] args) {
    Season s1 = Season.SPRING;
    System.out.println(s1); // 季节名:春天    描述:春暖花开 【因为重写了toString 方法】
    System.out.println(s1.name()); // SPRING

    Season s2 = Season.valueOf("SUMMER");
    System.out.println(s2); // 季节名:夏天    描述:炎炎盛夏

    System.out.println();

    Season[] seasons = Season.values(); // 得到一个Season类型的数组,数组元素为所有已经定义过的对象
    for (int i = 0; i < seasons.length; i++) {
      System.out.println(seasons[i]);
    }

    Season s3 = Season.WINTER;
    System.out.println(s3.ordinal()); // 3 【从0开始】
 }
}

enum Season {
  SPRING("春天", "春暖花开"), SUMMER("夏天", "炎炎盛夏"), AUTUMN("秋天", "秋高气爽"), WINTER("冬天", "白雪皑皑");

  private String name; // 季节名
  private String desc; // 季节描述

  private Season(String name, String desc) {
    this.name = name;
    this.desc = desc;
  }

  @Override
  public String toString() {
    return "季节名:" + name + "\t描述:" + desc;
  }
}

⑸ 让枚举类实现接口

语法:
enum 枚举类名 implements 接口名1, 接口名2 {
     // 实现接口的抽象方法
}

实现接口的抽象方法的方式:
      ① 直接在枚举类中实现抽象方法
      ② 在对象处实现抽象方法
          语法:
              对象名(){
                  // 实现接口的抽象方法
               }
示例:

public class Test {
  public static void main(String[] args) {
    Gender man = Gender.MALE;
    man.work(); // 男人工作

    Gender woman = Gender.FEMALE;
    woman.work(); // 人工作
  }
}

interface Work {
  void work();
}

enum Gender implements Work {
  MALE(){
    @Override
    public void work() { // 在对象处实现抽象方法
      System.out.println("男人工作");
    }
  }, FEMALE();

  @Override
  public void work() { // 直接在枚举类中实现抽象方法
    System.out.println("人工作");
  }
}

10、注解


⑴ 概念


注解又称为元数据,可以修饰类、属性、方法、构造器、包、参数和局部变量。其用于解释和说明被修饰的数据。
注解类似于注释,用于说明和解释被修饰的数据,但是和注释不同的是,它可以被编译和运行。

⑵ JDK内置的三种基本注解


① @Override 代表被修饰的方法为重写方法,如果重写的方法签名不符合语法规定,则报编译错误。它只能用于修饰方法
② @Deprecated 代表被修饰的数据为过时的,提醒调用方可以选择更佳方案。
③ @SuppressWarnings 抑制编译警告

⑶ 自定义注解


语法


修饰符 @interface 注解名 {
      类型 方法名();
}

注解的方法的注意事项

① 方法的类型为:八大基本数据类型、String、Class、枚举类型(用enum修饰的类),以及这些类型的数组类型
② 方法名如果为value,则调用注解时,可以省略 【即 @注解名(值)】。建议方法名为value
③ 可以给方法添加默认值,则调用注解时,可以不赋值。语法:类型 方法名( ) default 默认值;

注解调用的语法

在要被修饰的数据上方,添加注解
简单类型的注解 @注解名(方法名 = 值)
数组类型的注解 @注解名(方法名 = {值, 值})

⑷ 元注解


@Rentation


保留策略,指定注解可以保留多久
可选的属性保存在RetentionPolicy枚举类中
    SOURCE:保留到源代码
    CLASS:保留到字节码文件(.class)
    RUNTIME:保留到运行

@Target


指定注解可以修饰什么数据
可选的属性保存在ElementType枚举类中
    TYPE 类、接口(包括注释类型)或枚举声明
    FIELD 属性【字段声明(包括枚举常量)】
    METHOD 方法
    PARAMETER 参数
    CONSTRUCTOR 构造方法
    LOCAL_VARIABLE 局部变量
    ANNOTATION_TYPE 注释
    PACKAGE 包

@Documented


定义的注解能否在JavaDoc文档中显示

@Inherited


指示定义的注解被自动继承

示例:定义一个可以修饰类、属性和方法,可以显示在JavaDoc文档中,名叫MyAnnotation的注解。它有一个类型为String数组、名为name的方法,其中默认值为“张三”。它被保存到class文件中。

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@MyAnnotation
public class Test {
  @MyAnnotation(name = { "赵柳" })
  private int age;

  @MyAnnotation(name = { "李四", "王五" })
  public void method() {
  }
}

@Documented
@Target({ ElementType.TYPE, ElementType.FIELD, ElementType.METHOD })
@Retention(RetentionPolicy.CLASS)
@interface MyAnnotation {
  String[] name() default { "张三" };
}

相关文章

    暂无相关文章

用户点评