Java面向对象编程----面向对象(五)

64 阅读7分钟

单例模式

javaSE标准类中,java.lang.Runtime就是经典的单例模式。单例模式确保一个类中只有一个实例化对象产生。
构造器私有化,防止直接new,同时向外暴露一个静态的公共方法,用来返回单例对象。

饿汉式单例模式,类加载时实例化对象,如果没使用到这些对象,就会存在浪费资源的可能。 懒汉式单例模式,第一次使用时实例化。

饿汉式不存在线程安全问题,懒汉式存在线程安全问题。

多例设计:一个类可以有多个实例化对象。

枚举

枚举也是基于多例设计的,但它更为简洁,使用enum关键字定义的的类默认会继承Enum抽象类,同时Enum还是一个final类。
枚举对象必须定义在首行.
使用无参构造器创建枚举对象,则实参列表和小括号都可以省略.

enum Theme {
    RED,
    BLUE,
    GREEN;// 枚举对象必须在枚举类行首

    private String title;
}

enum Color{
    RED("红");
    private String title;
    Color(String title) {
        this.title = title;
    }
}

public class EnumDemo {
    public static void main(String[] args) {
        Theme val = Theme.RED;
        switch (val){ //Switch支持枚举
            case RED:
                System.out.println("红色");
            case BLUE:
                System.out.println("蓝色");
            case GREEN:
                System.out.println("绿色");
        }

        for (Theme t: Theme.values()) {
            System.out.println(t.ordinal() + ", " + t.name());
        }
    }
}

toString:Enum 类已经重写过了,返回的是当前对象名,子类可以重写该方法,用于返回对象的属性信息。
name:返回当前对象名(常量名),子类中不能重写.
ordinal:返回当前对象的位置号,默认从 0 开始.
values:返回当前枚举类中所有的常量.
valueOf:将字符串转换成枚举对象,要求字符串必须为已有的常量名,否则报异常!.
compareTo:比较两个枚举常量,比较的就是编号!

异常

它发生在程序运行期间,干扰了正常的指令流程。

Throwable 可以用来表示任何可以作为异常抛出的类,分为两种: ErrorException。其中Error用来表示JVM无法处理的错误,Exception 分为两种:

  • 受检异常 : 需要用 try...catch... 语句捕获并进行处理,并且可以从异常中恢复;
  • 非受检异常 : 是程序运行时错误,例如除 0 会引发 Arithmetic Exception,此时程序崩溃并且无法恢复。

image.png

常见运行时异常

程序的异常可以不做强制性处理

NullPointerException 空指针异常
ArithmeticException 数学运算异常
ArrayIndexOutOfBoundsException 数组下标越界异常
ClassCastException 类型转换异常
NumberFormatException 数字格式不正确异常

常见编译异常

是RuntimeException以外的异常,类型上都属于Exception类及其子类. IOException、SQLException等以及用户自定义的Exception异常.

异常捕获

try-catch-finally

异常发生,则异常发生后面的代码不会执行,直接进入catch块,异常没有发生,则顺序执行try的代码块,不会进入到catch.最后一定会执行finally里面的语句

不管是否发生异常,都执行(比如关闭连接,释放资源等),- finally {}

可以有多个catch语句,捕获不同的异常(进行不同的业务处理),要求父类异常在后,子类异常在前,(Exception 在后,NullPointerException 在前),如果发生异常,只会匹配一个catch。

try {
    System.out.println("产生异常" + (1/0));
}catch (ArithmeticException e){
    System.out.println(e);
    System.out.print("获取完整异常信息");
    e.printStackTrace();
}finally{
    System.out.println("无论是否抛异常都执行");
}

throws异常处理

方法可能生成某种异常,但是并不确定如何处理,可以通过throws显示地声明抛出异常,表明该方法将不对这些异常进行处理,而由该方法的调用者负责处理。

在方法声明中用throws语句可以声明抛出异常的列表,throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父类。

对于编译异常,程序中必须处理,比如try-catch或者throws, 对于运行时异常,程序中如果没有处理,默认就是throws的方式处理.

重写方法时,子类重写的方法,所抛异常要么和父类异常一致,要么为父类抛出的异常类型的子类型。

public static void method() throws IOException, FileNotFoundException{
    // ...
}

throw

在代码块中手动的抛出异常,而throws是在方法定义时,将程序可以出现的异常告知给调用者。

public static double method(int value) {
    if(value == 0) {
        throw new ArithmeticException("参数不能为0"); //抛出一个运行时异常
    }
    return 5.0 / value;
}

自定义异常

自定义异常类可继承Exception或RuntimeException

一个异常类应包含两个构造函数,一个无参构造函数和一个带有详细描述信息的构造函数(Throwable 的 toString方法会打印这些详细信息)

public class MyException extends Exception {
    public MyException(){ }
    public MyException(String msg){
        super(msg);
    }
    // ...
}

assert断言

运行程序时需要加入-ea 参数

int x = 10;
x = x * 10;
assert x == 100;// 断言x

内部类

可以直接访问私有属性,并且可以体现类与类之间的包含关系

分类

  • 定义在外部类局部位置上:
    • 局部内部类(有类名)
    • 匿名内部类(没有类名
  • 定义在外部类的成员位置上:
    • 成员内部类(没有static修饰)
    • 静态内部类(使用static修饰)
public class OuterDemo {
    public static void main(String[] args) {
        Outer o = new Outer();
        //实例化静态内部类
        Outer.StaticInner si = new Outer.StaticInner();
        si.print();

        // 内部接口
        IConnect.connect((IConnect.IWay) new ConnectImpl());
        
        new Outer().run(23893490);

    }
}

class Outer{
    static String name = "nangong";

    private String msg = "外部类";

    public void run(final long time){ // jdk1.8之前需要加final,方法内部类才能访问到方法的参数
        class MethodInner{ // 方法内部类
            public void print(){
                System.out.println("方法内部类, " + Outer.name);
                System.out.println(time);
            }
        }

        new MethodInner().print();// 方法内部类直接实例化
    }
    class Inner{ //内部类

    }
    static class StaticInner{
    // 静态内部类,只能访问static属性、方法
        public void print(){
            System.out.println(Outer.name);
        }
    }
}

interface IConnect{
    public void send(String str);
    static interface IWay{
        public Boolean walk();
    }

    public static void connect(IWay way){
        if(way.walk()){
            System.out.println("连接");
        }
    }
}

class ConnectImpl implements IConnect.IWay {
     public Boolean walk(){
         return true;
     }
}

局部内部类

局部内部类是定义在外部类的局部位置, 比如方法中,并且有类名。

不能添加访问修饰符。但是可以使用final修饰,因为它的地位就是一个局部变量。局部变量是不能使用修饰符的。但是可以使用final修饰,因为局部变量也可以使用final

匿名内部类

  • 本质还是类,内部类,该类没有名字,同时还是一个对象.\
  • 匿名内部类是定义在外部类的局部位置,比如方法中,并且没有类名\
  • 匿名内部类的语法比较奇特,因为匿名内部类既是一个类的定义,同时它本身也是一个对象,因此从语法上看,它既有定义类的特征,也有创建对象的特征,可以调用匿名内部类方法。
  • 可以直接访问外部类的所有成员,包含私有的。\
  • 不能添加访问修饰符,因为它的地位就是一个局部变量。\
  • 作用域:仅仅在定义它的方法或代码块中。\
  • 匿名内部类—访向---->外部类成员[访问方式:直接访问]。\
  • 外部其他类—不能访向---->匿名内部类(因为匿名内部类地位是一个局部变量)。\
  • 如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员) 去访问
//匿名内部类
IConnect ic = new IConnect() {
    @Override
    public void send(String str) {
        System.out.println("匿名内部类: " + str);
    }
};
ic.send("message");

Lamda表达式

函数式编程,简化代码 接口中只有函数式接口,才可以使用lamda表达式(有且只有一个抽象方法)

interface IMath{
    public Integer add(int a, int b);

}
public class LamdaDemo {
    public static void main(String[] args) {
        IMath m = (a1, a2) -> {
            return a1 + a2;
        };
        System.out.println(m.add(1, 4));
    }
}

方法引用

interface IMath{
    public Integer add(int a, int b);

}
//@FunctionalInterface //函数式接口
//interface IFunction<T>{
//    public T change(int a);
//}

//@FunctionalInterface //函数式接口
//interface IFunction<T>{
//    public T change();
//}

//@FunctionalInterface //函数式接口
//interface IFunction<T>{
//    public int change(T t1, T t2);
//}

class TestLamda{
    public TestLamda(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    private String name;
    private Integer age;
}
@FunctionalInterface //函数式接口
interface IFunction<T>{
    public T change(String a, int b);
}
public class LamdaDemo {
    public static void main(String[] args) {
        // 引用静态方法
//        IFunction<String> i = String :: valueOf;
//        String s = i.change(100);
//        System.out.println(s.length());

        //引用实例化对象操作方法
//        IFunction<String> fun = "instance" :: toUpperCase;
//        System.out.println(fun.change());

        // 引用类中指定方法
//        IFunction<String> method = String :: compareTo;
//        System.out.println(method.change("A", "a"));

        // 引用构造方法
        IFunction<TestLamda> m1 = TestLamda :: new;
        System.out.println(m1.change("A", 11));
    }
}

内建函数式接口

功能型函数接口、消费型函数接口、供给型函数接口、断言型函数式接口

import java.util.function.*;

public class LamdaDemo {
    public static void main(String[] args) {
        Function<String,Boolean> f1 = "**test"::startsWith;
        System.out.println(f1.apply("**"));
    }
}