JavaSE部分——面向对象(下)

面向对象(下)

一、Java 8 增强的包装类

为了解决8种基本数据类型的变量不能当成Object类型变量使用的问题,Java提供了包装类(Wrapper Class)的概念,为8种基本数据类型分别定义了相应的引用类型,并称之为基本数据类型的包装类。

图片说明

  • 自动装箱:把一个基本类型变量直接赋给对应的包装类变量,或者赋给Object变量(Object是所有类的父类,子类对象可以直接赋给父类变量)
  • 自动拆箱:允许直接把包装类对象直接赋给一个对应的基本类型变量

把字符串类型的值转换为基本类型的值有两种方式

  • 利用包装类提供的parseXxx(String s)静态方法(除 Character 之外的所有包装类都提供了该方法)
  • 利用包装类提供的valueOf(String s)静态方法
  • String类也提供了多个重载valueOf()方法,用于将基本类型变量转换成字符串

代码示例

public class Primitive2String {
    public static void main(String[] args) {
        String intStr = "123";
        // 把一个特定字符串转换成int变量
        int it1 = Integer.parseInt(intStr);
        int it2 = Integer.valueOf(intStr);
        System.out.println(it2);
        String floatStr = "4.56";
        // 把一个特定字符串转换成float变量
        float ft1 = Float.parseFloat(floatStr);
        float ft2 = Float.valueOf(floatStr);
        System.out.println(ft2);
        // 把一个float变量转换成String变量
        String ftStr = String.valueOf(2.345f);
        System.out.println(ftStr);
        // 把一个double变量转换成String变量
        String dbStr = String.valueOf(2.344);
        System.out.println(dbStr);
        // 把一个boolean变量转换成String变量
        String boolStr = String.valueOf(true);
        System.out.println(boolStr);
    }
}

基本类型变量和""进行连接运算,系统会自动把基本类型变量转换成字符串

//intStr的值为"5"
String intStr = 5 + "";

比较两个基本类型值的大小——静态的compare(xxx val1,xxx val2)

  • static String toUnsignedString(int/long i):int或long型整数转换为无符号整数对应的字符串
  • static String toUnsignedString(int/long i,int radix):int或long型整数转换为指定进制的无符号整数对应的字符串
  • static xxx oarseUnsignedXxx(String s):指定字符串解析成无符号整数
  • static xxx parseUnsignedXxx(String s,int radix):指定字符串按指定进制解析成无符号整数
  • static int compareUnsigned(xxx x,xxx y):将x、y两个整数转换为无符号整数后比较大小
  • static long divideUnsigned(long dividend,long divisor):将x、y两个整数转换为无符号整数后计算他们相除的商
  • static long remainderUnsigned(long dividend,long divisor):将x、y两个整数转换为无符号整数后计算他们相除的余数

二、处理对象

1、打印对象和toString方法

  • Object类提供的toString()方法总是返回该对象实现类的"类名+@+hashCode"值

重写Object类的toString()方法

class Apple {
    private String color;
    private double weight;

    public Apple() {
    }

    // 提供有参数的构造器
    public Apple(String color, double weight) {
        this.color = color;
        this.weight = weight;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public double getWeight() {
        return weight;
    }

    public void setWeight(double weight) {
        this.weight = weight;
    }

    // 重写toString()方法,用于实现Apple对象的“自我描述”
    public String toString() {
        return "一个苹果,颜色是: " + color + ",重量是: " + weight;
    }
}

public class ToStringTest {
    public static void main(String[] args) {
        Apple a = new Apple("红色", 5.68);
        // 打印Apple对象
        System.out.println(a);
    }
}

2、==和equals方法

判断两个变量是否相等有两种方式

  • ==运算符:如果两个变量是基本类型变量,且都是数据类型,则只要两个变量的值相等,就将返回true;对于两个引用类型变量,只有它们指向同一个对象时,==判断才会返回true,==不可用于比较类型上没有父子关系的两个对象
  • equals()方法:Object默认提供的equals()只是比较对象的地址
    重写equals()方法应满足下列条件:
    • 自反性:对任意x,x.equals(x)一定返回true
    • 对称性:对任意x和y,如果y.equals(x)返回true,则x.equals(y)也返回true
    • 传递性:对任意x,y,z,如果x.equals(y)返回true,y.equals(z)返回true,则x.equals(z)一定返回true
    • 一致性:对任意x和y,如果对象中用于等价比较的信息没有改变,那么无论调用x.equals(y)多少次,返回的结果应该保持一致,要么一直true,要么一直false
    • 对任何不是null的x,x.equals(null)一定返回false

三、类成员

1、理解类成员

2、单例(Singleton)类

如果一个类始终只能创建一个实例,则这个类被称为单例类。

代码示例

class Singleton {
    // 使用一个类变量来缓存曾经创建的实例
    private static Singleton instance;

    // 对构造器使用private修饰,隐藏该构造器
    private Singleton() {
    }

    // 提供一个静态方法,用于返回Singleton实例
    // 该方法可以加入自定义控制,保证只产生一个Singleton对象
    public static Singleton getInstance() {
        // 如果instance为null,则表明还不曾创建Singleton对象
        // 如果instance不为null,则表明已经创建了Singleton对象
        // 将不会重新创建新的实例
        if (instance == null) {
            // 创建一个Singleton对象,并将其缓存起来
            instance = new Singleton();
        }
        return instance;
    }
}

public class SingletonTest {
    public static void main(String[] args) {
        // 创建Singleton对象不能通过构造器
        // 只能通过getInstance方法来得到实例
        Singleton s1 = Singleton.getInstance();
        Singleton s2 = Singleton.getInstance();
        System.out.println(s1 == s2);// 将输出true
    }
}

四、final修饰符

final关键字可用于修饰类、变量和方法

  • 修饰变量时,表示该变量一旦获得了初始值就不可被改变

1、final成员变量

  • final修饰的成员变量必须由程序员显示地指定初始值
    • 类变量:必须在静态初始化块中指定初始值或声明该类变量时指定初始值,而且只能在两个地方的其中之一指定
    • 实例变量:必须在非静态初始化块声明该实例变量构造器中指定初始值,而且只能在三个地方的其中之一指定

2、final局部变量

public class FinalLocalVariableTest {
    public void test(final int a) {
        // 不能对final修饰的形参赋值,下面语句非法
        // a=5;
    }

    public static void main(String[] args) {
        // 定义final局部变量时指定默认值,则str变量无法重新复制
        final String str = "hello";
        // 下面赋值语句非法
        // str = "Java";
        // 定义final局部变量时没有指定默认值,则d变量可被赋值一次
        final double d;
        // 第一次赋初始值,成功
        d = 5.6;
        // 对final变量重复赋值,下面语句非法
        // d = 3.4;
    }
}

3、final修饰基本类型变量和引用类型变量的区别

  • 当使用final修饰基本类型变量时,不能对基本类型变量重新赋值,因此基本类型变量不能被改变。
  • 对于引用类型变量而言,它保存的仅仅是一个引用,final只保证这个引用类型变量所引用的地址不会改变,即一直引用同一个对象,但这个对象完全可以发生改变

代码示例

import java.util.Arrays;

class Person {
    private int age;

    public Person() {
    }

    // 有参数的构造器
    public Person(int age) {
        this.age = age;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

}

public class FinalReferenceTest {

    public static void main(String[] args) {
        // final修饰数组变量,iArr是一个引用变量
        final int[] iArr = { 5, 6, 12, 9 };
        System.out.println(Arrays.toString(iArr));
        // 对数组元素进行排序,合法
        Arrays.sort(iArr);
        System.out.println(Arrays.toString(iArr));
        // 对数组元素赋值,合法
        iArr[2] = -8;
        System.out.println(Arrays.toString(iArr));
        // 下面语句对iArr重新赋值,非法
        // iArr = null;
        // final修饰Person变量,p是一个引用变量
        final Person p = new Person(45);
        // 改变Person对象的age实例变量,合法
        p.setAge(23);
        System.out.println(p.getAge());
        // 下面语句对p重新赋值,非法
        // p = null;
    }
}

4、final方法

  • final修饰的方法不可被重写

5、final类

  • final修饰的类不可以有子类

6、不可变类

  • 创建该类的实例后,该实例的实例变量是不可改变的
  • 创建自定义的不可变类:
    • 使用private和final修饰符来修饰该类的成员变量
    • 提供带参数构造器,用于根据传入参数来初始化类的成员变量
    • 仅为该类的成员变量提供getter方法,不要为该类的成员变量提供setter方法
    • 如果有必要,重写Object类的hashCode()和equals()方法。equals()方法根据关键成员变量来作为两个对象是否相等的标砖,除此之外,还应该保证两个equals()方法判断为相等的对象的hashCode()也相等。

代码示例

public class Address {
    private final String detail;
    private final String postCode;

    // 在构造器里初始化两个实例变量
    public Address() {
        this.detail = "";
        this.postCode = "";
    }

    public Address(String detail, String postCode) {
        this.detail = detail;
        this.postCode = postCode;
    }

    // 仅为两个实例变量提供getter方法
    public String getDetail() {
        return detail;
    }

    public String getPostCode() {
        return postCode;
    }

    // 重写equals()方法,判断两个对象是否相等
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj != null && obj.getClass() == Address.class) {
            Address ad = (Address) obj;
            // 当detail和postCode相等时,可认为两个Address对象相等
            if (this.getDetail().equals(ad.getDetail()) 
                    && this.getPostCode().equals(ad.getPostCode())) {
                return true;
            }
        }
        return false;
    }

    public int hashCode() {
        return detail.hashCode() + postCode.hashCode() * 31;
    }
}

7、缓存实例的不可变类

  • 如果程序经常需要使用相同的不可变类实例,则应该考虑缓存这种不可变类的实例

使用数组作为缓存池,实现一个缓存实例的不可变类

class ChacheImmutale {
    private static int MAX_SIZE = 10;
    // 使用数组来缓存已有的实例
    private static ChacheImmutale[] cache = new ChacheImmutale[MAX_SIZE];
    // 记录缓存实例在缓存中的位置,cache[pos-1]是最新缓存的实例
    private static int pos = 0;
    private final String name;

    private ChacheImmutale(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public static ChacheImmutale valueOf(String name) {
        // 遍历已缓存的对象
        for (int i = 0; i < MAX_SIZE; i++) {
            // 如果已有相同实例,则直接返回该缓存的实例
            if (cache[i] != null && cache[i].getName().equals(name)) {
                return cache[i];
            }
        }
        // 如果缓存池已满
        if (pos == MAX_SIZE) {
            // 把缓存的第一个对象覆盖,即把刚刚生成的对象放在缓存池的最开始位置
            cache[0] = new ChacheImmutale(name);
            // 把pos设为1
            pos = 1;
        } else {
            // 把新创建的对象缓存起来,pos加1
            cache[pos++] = new ChacheImmutale(name);
        }
        return cache[pos - 1];
    }

    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj != null && obj.getClass() == ChacheImmutale.class) {
            ChacheImmutale ci = (ChacheImmutale) obj;
            return name.equals(ci.getName());
        }
        return false;
    }

    public int hashCode() {
        return name.hashCode();
    }
}

public class ChacheImmutaleTest {
    public static void main(String[] args) {
        ChacheImmutale c1 = ChacheImmutale.valueOf("hello");
        ChacheImmutale c2 = ChacheImmutale.valueOf("hello");
        // 下面代码将输出true
        System.out.println(c1 == c2);
    }
}

Integer类构造器和valueOf()方法存在的差异

public class IntegerTest {
    public static void main(String[] args) {
        // 生成新的Integer对象
        Integer in1 = new Integer(6);
        // 生成新的Integer对象,并缓存该对象
        Integer in2 = Integer.valueOf(6);
        // 直接从缓存中取出Integer对象
        Integer in3 = Integer.valueOf(6);
        System.out.println(in1 == in2);// 输出false
        System.out.println(in2 == in3);// 输出true
        // 由于Integer只缓存-128~127之间的值
        // 因此200对应的Integer对象没有被缓存
        Integer in4 = Integer.valueOf(200);
        Integer in5 = Integer.valueOf(200);
        System.out.println(in4 == in5);// 输出false
    }
}

五、抽象类

1、抽象方法和抽象类

  • 抽象方法和抽象类必须使用abstract修饰符来定义,有抽象方法的类只能被定义成抽象类,抽象类里可以没有抽象方法
  • 抽象方法和抽象类的规则:
    • 抽象方法和抽象类必须使用abstract修饰符来定义,抽象方法不能有方法体
    • 抽象类不能被实例化
    • 抽象类可以包含成员变量、方法(普通方法和抽象方法)、构造器、初始化块、内部类(接口、枚举)5种成分
    • 含有抽象方法的类只能被定义成抽象类

Shape抽象类

public abstract class Shape {
    {
        System.out.println("执行Shape的初始化块...");
    }
    private String color;

    // 定义一个计算周长的抽象方法
    public abstract double calPerimeter();

    // 定义一个返回形状的抽象方法
    public abstract String getType();

    // 定义Shape的构造器,该构造器并不是用于创建Shape对象
    // 而是用于被子类调用
    public Shape() {
    }

    public Shape(String color) {
        System.out.println("执行Shape的构造器...");
        this.color = color;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

}

定义一个三角形类

public class Triangle extends Shape {
    // 定义三角形的三边
    private double a;
    private double b;
    private double c;

    public Triangle(String color, double a, double b, double c) {
        super(color);
        setSides(a, b, c);
    }

    public void setSides(double a, double b, double c) {
        if (a >= b + c || b >= a + c || c >= a + b) {
            System.out.println("三角形两边之和必须大于第三边");
            return;
        }
        this.a = a;
        this.b = b;
        this.c = c;
    }

    // 重写Shape类的计算周长的抽象方法
    public double calPerimeter() {
        return a + b + c;
    }

    // 重写Shape类的返回形状的抽象方法
    public String getType() {
        return "三角形";
    }

}

定义一个Circle普通类

public class Circle extends Shape {
    private double radius;

    public Circle(String color, double radius) {
        super(color);
        this.radius = radius;
    }

    public void setRadius(double radius) {
        this.radius = radius;
    }

    // 重写Shape类的计算周长的抽象方法
    public double calPerimeter() {
        return 2 * Math.PI * radius;
    }

    // 重写Shape类的返回形状的抽象方法
    public String getType() {
        return getColor() + "圆形";
    }

    public static void main(String[] args) {
        Shape s1 = new Triangle("黑色", 3, 4, 5);
        Shape s2 = new Circle("黄色", 3);
        System.out.println(s1.getType());
        System.out.println(s1.calPerimeter());
        System.out.println(s2.getType());
        System.out.println(s2.calPerimeter());
    }
}

注意:abstract不能用于修饰成员变量和局部变量,也不能用于修饰构造器。static和abstract不能同时修饰某个方法,即没有所谓的类抽象方法,但它们可以同时修饰内部类。abstract修饰的方法必须被其子类重写,否则这个方法将永远不会有方法体,因此abstract方法不能定义为private访问权限,即private和abstract不能同时修饰方法。

2、抽象类的作用

六、Java 9 改进的接口

1、接口的概念

  • 接口定义的是多个共同的公共行为规范,这些行为是与外部交流的通道,这就意味着接口里通常是定义一组公用方法

2、Java 9 中接口的定义

[修饰符] interface 接口名 extends 父接口1,父接口2...
{
    零个到多个常量定义...
    零个到多个抽象方法定义...
    零个到多个内部类、接口、枚举定义...
    零个到多个私有方法、默认方法或类方法定义...
}
  • 接口里不能包含构造器和初始化块定义

  • 接口里定义成员变量只能在定义时指定默认值,public static final修饰符可省略

    public interface Output {
      // 接口里定义的成员变量只能是常量
      int MAX_CACHE_LINE = 50;
    
      // 接口里定义的普通方法只能是public的抽象方法
      void out();
    
      void getData(String msg);
    
      // 在接口中定义默认方法,需要使用default修饰
      default void print(String... msgs) {
          for (String msg : msgs) {
              System.out.println(msg);
          }
      }
    
      // 在接口中定义默认方法,需要使用default修饰
      default void test() {
          System.out.println("默认的test()方法");
      }
    
      // 在接口中定义类方法,需要使用static修饰
      static String staticTest() {
          return "接口里的类方法";
      }
    
      // 定义私有方法
      private void foo() {
          System.out.println("foo 私有方法");
      }
    
      // 定义私有静态方法
      private static void bar() {
          System.out.println("bar 私有静态方法");
      }
    }

3、接口的继承

interface InterfaceA {
    int PROP_A = 5;

    void testA();
}

interface InterfaceB {
    int PROP_B = 6;

    void testB();
}

interface InterfaceC extends InterfaceA, InterfaceB {
    int PROP_C = 7;

    void testC();
}

public class InterfaceExtendsTest {
    public static void main(String[] args) {
        System.out.println(InterfaceC.PROP_A);
        System.out.println(InterfaceC.PROP_B);
        System.out.println(InterfaceC.PROP_C);
    }
}

4、使用接口

[修饰符] class 类名 extends 父类 implements 接口1,接口2...
{
    类体部分
}

代码示例

//定义一个Product接口
interface Product {
    int getProduceTime();
}

//让Printer类实现Output和Product接口
public class Printer implements Output, Product {
    private String[] printData = new String[MAX_CACHE_LINE];
    // 用以记录当前需打印的作业数
    private int dataNum = 0;

    public void out() {
        // 只要还有作业,就继续打印
        while (dataNum > 0) {
            System.out.println("打印机打印: " + printData[0]);
            // 把作业队列整体前移一位,并将剩下的作业数减1
            System.arraycopy(printData, 1, printData, 0, --dataNum);
        }
    }

    public void getData(String msg) {
        if (dataNum >= MAX_CACHE_LINE) {
            System.out.println("输出队列已满,添加失败");
        } else {
            // 把打印数据添加到队列里,已保存数据的数量加1
            printData[dataNum++] = msg;
        }
    }

    public int getProduceTime() {
        return 45;
    }

    public static void main(String[] args) {
        // 创建一个Printer对象,当成Output使用
        Output o = new Printer();
        o.getData("轻量级Java EE企业应用实战");
        o.getData("疯狂Java讲义");
        o.out();
        o.getData("疯狂Android讲义");
        o.getData("疯狂Ajax讲义");
        o.out();
        // 调用Output接口中定义的默认方法
        o.print("孙悟空", "猪八戒", "白骨精");
        o.test();
        // 闯将一个Printer对象,当成Product使用
        Product p = new Printer();
        System.out.println(p.getProduceTime());
        // 所有接口类型的引用变量都可直接赋给Object类型的变量
        Object obj = p;
    }
}

5、接口和抽象类

  • 相同点
    • 接口和抽象类都不能被实例化
    • 接口和抽象类都可以包含抽象方法,实现接口或继承抽象类的普通子类都必须事先这些抽象方法
  • 不同点
    • 接口里只能包含抽象方法、静态方法、默认方法和私有方法,不能为普通方法提供方法实现;抽象类则完全可以包含普通方法
    • 接口里只能定义静态常量,不能定义普通成员变量;抽象类里则既可以定义普通成员变量,也可以定义静态常量
    • 接口里不包含构造器;抽象类里可以包含构造器,抽象类里的构造器并不是用于创建对象,而是让其子类调用这些构造器来完成属于抽象类的初始化操作
    • 接口里不能包含初始化块;但抽象类则完全可以包含初始化块
    • 一个类最多只能有一个直接父类,包括抽象类;但一个类可以直接实现多个接口,通过实现多个接口可以弥补Java单继承的不足

6、面向接口编程

  • 简单工厂模式
  • 命令模式

七、内部类

1、非静态内部类

  • 外部类2个作用域:public和省略访问控制符
  • 内部类4个作用域
  • 非静态内部类里不能有静态方法、静态成员变量、静态初始化块
public class Cow {
    private double weight;

    // 外部类的两个重载的构造器
    public Cow() {
    }

    public Cow(double weight) {
        this.weight = weight;
    }

    // 定义一个非静态内部类
    private class CowLeg {
        // 非晶态内部类的两个实例变量
        private double length;
        private String color;

        // 非静态内部类的两个重载的构造器
        public CowLeg() {
        }

        public CowLeg(double length, String color) {
            this.length = length;
            this.color = color;
        }

        public double getLength() {
            return length;
        }

        public void setLength(double length) {
            this.length = length;
        }

        public String getColor() {
            return color;
        }

        public void setColor(String color) {
            this.color = color;
        }

        // 非静态内部类的实例方法
        public void info() {
            System.out.println("当前牛腿颜色是: " + color + ",高: " + length);
            // 直接访问外部类的private修饰的成员变量
            System.out.println("本牛腿所在奶牛重: " + weight);
        }
    }

    public void test() {
        CowLeg c1 = new CowLeg(1.12, "黑白相间");
        c1.info();
    }

    public static void main(String[] args) {
        Cow cow = new Cow(378.9);
        cow.test();
    }
}
  • 外部类成员变量、内部类成员变量与内部类里方法的局部变量同名,则可以通过使用this、外部类类名.this作为限定区分
public class DiscernVariable {
    private String prop = "外部类的实例变量";

    public class InClass {
        private String prop = "内部类的实例变量";

        public void info() {
            String prop = "局部变量";
            // 通过外部类类名.this.varName访问外部类实例变量
            System.out.println("外部类的实例变量值: " + DiscernVariable.this.prop);
            // 通过this.varName访问内部类实例的变量
            System.out.println("内部类的实例变量值: " + this.prop);
            // 直接访问局部变量
            System.out.println("局部变量的值: " + prop);
        }
    }

    public void test() {
        InClass in = new InClass();
        in.info();
    }

    public static void main(String[] args) {
        new DiscernVariable().test();
    }
}
  • 非静态内部类的成员可以访问外部类的private成员,但反过来就不成立了
public class Outer {
    private int outProp = 9;

    class Inner {
        private String inProp = "内部类的实例变量";

        public void accessOuterProp() {
            // 非静态内部类可以直接访问外部类的private成员变量
            System.out.println("外部类的outProp值: " + outProp);
        }
    }

    public void accessInnerProp() {
        // 外部类不能直接访问非静态内部类的实例变量
        // 下面代码出现编译错误
        // System.out.println("内部类的inProp值: " + inProp);
        // 如需访问内部类的实例变量,必须显示创建内部类对象
        System.out.println("内部类的inProp值: " + new Inner().inProp);
    }

    public static void main(String[] args) {
        // 执行下面代码,值创建了外部类对象,还未创建内部类对象
        Outer out = new Outer();
        out.accessInnerProp();
    }
}

2、静态内部类

  • 静态内部类可以包含静态成员,也可以包含非静态成员
public class AccessStaticInnerClass {

    static class StaticInnerClass {
        private static int prop1 = 5;
        private int prop2 = 9;
    }

    public void accessInnerProp() {
        // System.out.println(prop1);
        // 上面代码出现错误,应改为如下形式
        // 通过类名访问静态内部类的类成员
        System.out.println(StaticInnerClass.prop1);
        // System.out.println(prop2);
        // 上面代码出现错误,应改为如下形式
        // 通过实例访问静态内部类的实例成员
        System.out.println(new StaticInnerClass().prop2);
    }
}
  • 接口内部类只能是静态内部类,访问控制符只能是public,默认使用public static

3、使用内部类

  • 在外部内内部使用内部类
  • 在外部类以外使用非静态内部类:OuterClass.InnerClass varName 、 OuterInstance.new InnerConstructor();
    • 如果希望在外部类以外的地方访问内部类(包括静态和非静态两种),则内部类不能使用private访问控制权限
class Out{
    //定义一个内部类,不使用访问控制符
    //即只有同一个包中的其他类可访问该内部类
    class In{
        public In(String msg) {
            System.out.println(msg);
        }
    }
}
public class CreateInnerInstance {
    public static void main(String[] args) {
//        Out.In in = new Out().new In("测试信息");
        Out.In in;
        Out out = new Out();
        in = out.new In("测试信息");
    }
}
  • 在外部类以外使用静态内部类:new OuterClass.InnerConstructor()
class StaticOut {
    // 定义一个静态内部类,不使用访问控制符
    // 即只有同一个包中的其他类可访问该内部类
    static class StaticIn {
        public StaticIn() {
            System.out.println("静态内部类的构造器");
        }
    }
}

public class CreateStaticInnerInstance {
    public static void main(String[] args) {
//        StaticOut.StaticIn in = new StaticOut.StaticIn();
        StaticOut.StaticIn in;
        in = new StaticOut.StaticIn();
    }
}

4、局部内部类

  • 不能使用访问控制符和static修饰符修饰
public class LocalInnerClass {
    public static void main(String[] args) {
        // 定义局部内部类
        class InnerBase {
            int a;
        }
        // 定义局部内部类的子类
        class InnerSub extends InnerBase {
            int b;
        }
        // 创建局部内部类的对象
        InnerSub is = new InnerSub();
        is.a = 5;
        is.b = 8;
        System.out.println("InnerSub对象的a和b实例变量是: " + is.a + "," + is.b);
    }
}

5、Java 8 改进的匿名内部类

  • 匿名内部类适合创建那种只需要一次使用的类,不能重复使用
  • 匿名内部类不能是抽象类
  • 匿名内部类不能定义构造器
new 实现接口() | 父类构造器(实参列表)
{
    //匿名内部类的类体部分
}

八、Java 8 新增的Lambda表达式

1、Lambda表达式入门

三部分组成

  • 形参列表。形参列表允许省略形参类型。如果形参列表中只有一个参数,甚至连形参列表的圆括号也可以省略
  • 箭头(->)。必须通过英文中画线和大于符号组成。
  • 代码块。如果代码块只包含一条语句,Lambda表达式允许省略代码块的花括号,那么这条语句就不要用花括号表示语句结束。Lambda代码块只有一条return语句,甚至可以省略return关键字。Lambda表达式需要返回值,而它的代码块中仅有一条省略了return的语句,Lambda表达式会自动返回这条语句的值。
interface Eatable {
    void taste();
}

interface Flyable {
    void fly(String weather);
}

interface Addable {
    int add(int a, int b);
}

public class LambdaQs {
    // 调用该方法余姚Eatable对象
    public void eat(Eatable e) {
        System.out.println(e);
        e.taste();
    }

    // 调用该方法需要Flyable对象
    public void drive(Flyable f) {
        System.out.println("我正在驾驶: " + f);
        f.fly("【碧空如洗的晴日】");
    }

    // 调用该方法需要Addable对象
    public void test(Addable add) {
        System.out.println("5与3的和为: " + add.add(5, 3));
    }

    public static void main(String[] args) {
        LambdaQs lq = new LambdaQs();
        // Lambda表达式的代码块只有一条语句,可以省略花括号
        lq.eat(() -> System.out.println("苹果的味道不错"));
        // Lambda表达式的形参列表只有一个形参,可以省略圆括号
        lq.drive(weather -> {
            System.out.println("今天天气是: " + weather);
            System.out.println("直升机飞行平稳");
        });
        // Lambda表达式的代码块只有一句话,可以省略花括号
        // 代码块中只有一条语句,即使表达式需要返回值,也可以省略return关键字
        lq.test((a, b) -> a + b);
    }
}

2、Lambda表达式与函数式接口

  • Lambda表达式的类型,也被称为“目标类型”,Lambda表达式的目标类型必须是“函数式接口”。函数式接口代表只包含一个抽象方法的接口。函数式接口可以包含多个默认方法、类方法,但只能声明一个抽象方法。

九、枚举类

1、枚举类入门

  • 枚举类可以实现一个或多个接口,使用enum定义的枚举类默认继承了java.lang.Enum类,而不是默认继承Object类,其中java.lang.Enum类实现了java.lang.Serializable和java.lang.Comparable两个接口
  • 使用enum定义、非抽象的枚举类默认会使用final修饰,因此枚举类不能派生子类
  • 枚举类的构造器只能使用private访问控制符,如果省略了构造器的访问控制符,则默认使用private修饰;如果强制指定访问控制符,则只能指定private修饰符
  • 枚举类的所有实例必须在枚举类的第一行显示列出,否则这个枚举类永远都不能产生实例。列出这些实例时,系统会自动添加public static final修饰,无须程序员显示添加

枚举类默认提供了一个values()方法,该方法可以很方便地遍历所有的枚举值

public enum SeasonEnum {
    //在第一行列出4个枚举实例
    SPRING,SUMMER,FALL,WINTER;
}

Test类

public class EnumTest {
    public void judge(SeasonEnum s) {
        // switch语句里的表达式可以是枚举值
        switch (s) {
        case SPRING:
            System.out.println("春");
            break;
        case SUMMER:
            System.out.println("夏");
            break;
        case FALL:
            System.out.println("秋");
            break;
        case WINTER:
            System.out.println("冬");
            break;
        }
    }

    public static void main(String[] args) {
        // 枚举类默认有一个values()方法,返回该枚举类的所有实例
        for (SeasonEnum s : SeasonEnum.values()) {
            System.out.println(s);
        }
        // 使用枚举实例时,可通过EnumClass.variable形式来访问
        new EnumTest().judge(SeasonEnum.SPRING);
    }
}

2、枚举类的成员变量、方法和构造器

public enum Gender {
    //此处的枚举值必须调用对应的构造器来创建
    MALE("男"),FEMALE("女");
    private final String name;
    //枚举类的构造器只能使用private修饰
    private Gender(String name){
        this.name = name;
    }
    public String getName() {
        return this.name;
    }
}

Test类

public class GenderTest {
    public static void main(String[] args) {
        // 通过Enum的valueOf()方法来获取指定枚举类的枚举值
        Gender g = Enum.valueOf(Gender.class, "FEMALE");
        g.setName("女");
        System.out.println(g + "代表: " + g.getName());
        // 此时设置name值时将会提示参数错误
        g.setName("男");
        System.out.println(g + "代表: " + g.getName());
    }
}

十、对象在内存中的状态

  • 可达状态:当一个对象被创建后,若有一个以上的引用变量引用它,则这个对象在程序中处于可达状态,程序可通过引用变量来调用该对象的实例变量和方法。
  • 可恢复状态:系统的垃圾回收机制准备回收该对象所占用的内存,在回收该对象之前,系统会调用所有可恢复状态对象的finalize()方法进行资源清理。如果系统在调用finalize()方法时重新让一个引用变量引用该对象,则这个对象会再次变为可达状态;否则该对象将进入不可达状态。
  • 不可达状态:当对象与所有引用变量的关联都被切断,且系统已经调用所有对象的finalize()方法后依然没有使该对象变成可达状态,那么这个对象将永久性地失去引用,最后变成不可达状态。只有当一个对象处于不可达状态是,系统才会真正回收该对象所占有的资源。

图片说明

2、强制垃圾回收

  • 调用System类的gc()静态方法:System.gc()。
  • 调用Runtime对象的gc实例方法:Runtime.getRuntime.gc()。

3、finalize方法

全部评论

相关推荐

一tiao酸菜鱼:秋招还没正式开始呢,就准备有结果了。。。。?
点赞 评论 收藏
分享
06-19 19:06
门头沟学院 Java
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务