22-04-21 西安 javaSE(12)枚举类enum,实例初始化加载过程,类的初始化过程,Throwable类、异常Exception

本文详细介绍了Java中的枚举类型,包括命名规范、构造器、枚举类方法、继承关系以及常量类的初始化过程。此外,还深入讨论了异常处理,包括异常的分类、处理关键字、异常声明、finally块的使用以及try-with-resources语句。同时,提到了自定义异常的创建,包括运行时异常和编译时异常。文章以实例展示了各种情况下的代码实现和执行过程。
摘要由CSDN通过智能技术生成

枚举类

1、命名规范

枚举是jdk5.0的特性,用于创建固定数量对象的简化方式,如月份,星期,季节类。使用关键字enum定义枚举类型

命名规范

  1. 枚举类名带上Enum后缀
  2. 枚举成员名称需要全部大写,单词间用下划线隔开
public enum DictEnum {

    HOSTYPE("Hostype", "医院等级"),
    CERTIFICATES_TYPE("CertificatesType", "证件类型");

    public String dictCode;
    public String msg;

    DictEnum(String dictCode, String msg) {
        this.dictCode = dictCode;
        this.msg = msg;
    }
}

注意:

  • 枚举类中对象创建的代码必须在枚举类的第一行,否则编译报错
  • 枚举类中对象隐式被 public static final关键字修饰,自己手动显示会编译报错

2、构造器

枚举类里外都不可以new对象

new  Gender();//会报错

只能通过枚举固定的格式创建对象。

枚举类获取对象的格式:枚举类名.对象名


public enum Gender {
    BOY(),GIRL();
}
public class EnumDemo {
    public static void main(String[] args) {
        Gender boy1 = Gender.BOY;//获取对象的格式:枚举类名.对象名
        Gender boy2 = Gender.BOY;
        System.out.println(boy1==boy2);//true
        Gender girl1 = Gender.GIRL;
        Gender girl2 = Gender.GIRL;
        System.out.println(girl1==girl2);//true

    }
}

枚举类的构造方法被默认强制私有

  1. 如果枚举类没有任何的构造器时,jvm会自动提供一个private的无参构造器,反之不提供
  2. 枚举类中的构造器权限访问级别只能是private,如果不写权限访问级别,jvm会自动补上private

 枚举类中对象如果使用无参构造器创建对象,对象名后面的()可以全部省略不写


public enum Gender {
    BOY(),GIRL();
}

public enum Gender {
    BOY,GIRL;
}

3、枚举类方法

values(), ordinal() 和 valueOf() 方法位于 java.lang.Enum 类中:

  • values() 返回所有的枚举值。
  • ordinal()方法可以找到每个枚举常量的索引,就像数组索引一样。
  • valueOf()方法返回指定字符串值的枚举常量。
public static void main(String[] args) {
    //枚举类默认有一个values()方法,返回该枚举类的所有实例
    for (DictEnum value : DictEnum.values()) {
        System.out.println(value);
    }
    //访问枚举实例
    System.out.println(DictEnum.HOSTYPE);
}

4、继承关系

  • 枚举类都默认继承Enum
  • 枚举类不能被其他类继承,构造器被私有化

程序中所有的枚举类都默认继承Enum,而不是默认继承Object类,枚举类再无法继承其他类,但是可以实现接口
在枚举类的构造器中无法显示使用super()      super is not allowed in enum constructor

Enum 中只有一个构造方法
protected Enum(String name,int ordinal)
程序员无法调用此构造方法。该构造方法用于由响应枚举类型声明的编译器发出的代码

只能说鬼斧神工

Gender boy = Gender.BOY;沙老师,为什么这行代码会调用俩次 Gender的无参构造器呀?

因为创建对象的代码在类的成员位置,所以当你用的时候,它随着类的加载就加载啦,但只加载一次,不会太浪费,有隐式的static呢。真是似懂非懂呢,结合下文类加载过程好像懂了

public enum Gender {
    BOY, GIRL;

    Gender() {
        System.out.println("Gender类无参构造器");
    }
}

class EnumDemo2 {
    public static void main(String[] args) {
        Gender boy = Gender.BOY;
    }
}


5、常量类

1

/**
 * @Description: 常量类
 */
public class Constants {
	/**配置文件名称*/
	public final static String CONFIGFILE="config";
	/**应用目录,所有应用程序都放在这个目录中*/
	public final static String APPFOLDER="app";
}

初始化

1、实例初始化加载过程

加载先后原则:

1.显示或隐式的super(实参)或显示的this(实参)
2.实例成员(实例变量显示赋值操作,实例常量,实例方法,实例内部类)和构造器代码块加载优先级一样,谁在前优先加载谁
3.再加载构造器中除了super(实参)或this(实参)的显示内容

1.无继承关系

public class Superclass {
        //实例变量
        int num=method();
        //实例方法
        private int method(){
            System.out.println("实例变量的显示赋值操作");
            return 10;
        }

        public Superclass(){
            System.out.println("无参构造器第三部分代码");
        }

        {
            System.out.println("构造器代码块");
        }
}
class EnumDemo3 {
    public static void main(String[] args) {
        Superclass superclass = new Superclass();
    }
}}

2,有继承关系 

public class Superclass {
        //实例变量
        int num=method();
        //实例方法
        private int method(){
            System.out.println("父类实例变量的显示赋值操作");
            return 10;
        }

        public Superclass(){
            System.out.println("父类无参构造器显示代码");
        }

        {
            System.out.println("父类构造器代码块");
        }
}

  class Subclass extends Superclass{
    //实例变量
    int num=method();
    //实例方法
    private int method(){
        System.out.println("子类实例变量的显示赋值操作");
        return 10;
    }

    public Subclass(){
        System.out.println("子类无参构造器显示代码");
    }

    {
        System.out.println("子类构造器代码块");
    }
}

class EnumDemo3 {
    public static void main(String[] args) {
        Subclass subclass = new Subclass();
    }
}


2、this关键字第三种用法

父类中使用this,this是父类类型的子类对象(多态)

场景:在父类的构造器中或者实例方法中
语法格式:
this.实例变量
this.实例方法(实参)

巨坑的题:

public class Superclass {
    //实例变量
    int num=10;
    //实例方法
    public void method(){
        System.out.println("num="+num);
        System.out.println("this"+this);
    }

    public Superclass(){
        this.method();//在父类中使用this,this是父类类型的子类对象(多态)
    }

}
class Subclass extends Superclass{
    //实例变量
    int num=20;
    //实例方法
    @Override
    public void method(){
        System.out.println("num="+num);
    }

    public Subclass(){
        super();
    }
}

 class EnumDemo {
    public static void main(String[] args) {
        Subclass subclass = new Subclass();//num=0
        //只创建了一个子类,子类才有地址值。父类并没有在堆开辟空间,父类中打印的this肯定不是父类的对象
    }
}


3、类的初始化过程

随着字节码文件的加载而加载,且只加载唯一的一次

原则:

静态成员(静态变量,静态常量,静态方法,静态内部类)和静态代码块加载优先级一样,谁在前加载谁,而且只加载唯一的一次。

1.无继承关系

 class Superclass{
    static int num= method();
    //实例方法
    public static int method(){
        System.out.println("静态变量的显示赋值操作");
        return 10;
    }
    static{
        System.out.println("静态代码块");
    }

}
class Demo{
    public static void main(String[] args) {
        new Superclass();
        new Superclass();
        new Superclass();
    }
}

2.有继承关系

public class Superclass {
    static int supernum = method();

    public static int method() {
        System.out.println("父类静态变量的显示赋值操作");
        return 10;
    }

    static {
        System.out.println("父类静态代码块");
    }
}


class Subclass extends Superclass {
    static int subnum = method1();

    public static int method1() {
        System.out.println("子类静态变量的显示赋值操作");
        return 10;
    }

    static {
        System.out.println("子类静态代码块");
    }
}

class Demo {
    public static void main(String[] args) {
        new Subclass();
        new Subclass();

    }
}


Throwable类

学习异常的好处:不能把服务器搞垮了,不能让jvm挂掉

Throwable :   XxxError或XxxException的父类 ,所有错误或异常的超类
     1.XxxError是无法通过程序的后续代码进行解决,只能事先避免
     2.Exception:程序中出现的不正常情况,最终导致jvm正常停止,这种情况可以通过程序的后续代码进行补救

Throwable类中常用的方法:

  1. Throwable(String message)   构造带指定详细消息的新 throwable。
  2. printStackTrace()   //打印Throwable对象的详细信息,红色字体
  3. getMessage()   //  返回Throwable构造器中给的字符串。

Exception

1、Exception的分类

Exception 类及其子类是 Throwable 的各种异常情况的分类。

Throwable 类常用方法
public string getMessage():返回异常发生时的简要描述

public string toString():返回异常发生时的详细信息

public string getLocalizedMessage():返回异常对象的本地化信息。使用 Throwable 的子类覆盖这个方法,可以生成本地化信息。如果子类没有覆盖该方法,则该方法返回的信息与 getMessage()返回的结果相同

public void printStackTrace():在控制台上打印 Throwable 对象封装的异常信息


Exception(String message)   构造带指定详细消息的新异常。

public Exception(String message) {
    //调用了父类Throwable的构造器
    super(message);
}

public Throwable(String message) {
    fillInStackTrace();
    detailMessage = message;
}

运行时异常RuntimeException及其子类

     特点:代码没有任何语法格式错误,但在运行期间出现了数据上的问题。如空指针异常

public class RuntimeException extends Exception {

 public RuntimeException(String message) {
     //最终还是调用到了Throwable中的构造器方法
     super(message);
 }

}

-------------------------

编译时异常: Exception及其子类(RuntimeException及其子类除外)
      特点:代码没有任何语法格式错误,但在编译期间出现了编译错误。如:FileInputStream stream = new FileInputStream("w");

运行时异常jvm会干啥
       jvm会调用异常对象的父类方法,打印堆栈信息。然后让程序立刻停止。【补充:jvm不仅帮你new异常,还要throw,抛给main方法。main方法没有进行异常处理就给调用者jvm。】

NoClassDefFoundError 和 ClassNotFoundException 有什么区别?

它们都是由于系统运行时找不到要加载的类导致的,但是触发的原因不一样。 

NoClassDefFoundError:程序在编译时可以找到所依赖的类,但是在运行时找不到指定的类文件,导致抛出该错误;原因可能是 jar 包缺失或者调用了初始化失败的类。
ClassNotFoundException:当动态加载 Class 对象的时候找不到对应的类时抛出该异常;原因可能是 要加载的类不存在或者类名写错了。

2、异常处理关键字

throws,try,catch,finally,以及异常处理的前提关键字 throw

异常声明 throws:(将异常信息在方法进行标记,交给调用者进行解决)

异常捕获:try,catch,finally   (将异常信息自己处理)

抛出一个异常对象:throw用在方法内,用来抛出一个异常对象,并结束当前方法

throws 关键字后面跟的是异常的名字;而 throw 关键字后面跟的是异常的对象

1.在程序中,无论是运行时异常还是编译时异常都必须解决。
2.运行时异常解决套路:

可以程序员手动解决,也可以程序员不解决,交给jvm进行解决。
手动解决:throws,try...catch
自动解决:jvm处理(调用异常对象的printStackTrace,终止jvm)

3.编译时异常必须由程序员手动解决。

throw关键字,抛出异常对象(让jvm识别到该异常对象)

throw new IOException("自定义IO异常对象");//会编译报错  
throw new RuntimeException("自定义运行时对象");//不会编译报错

异常声明throws 的注意点:

在方法内部不进行解决,在方法声明上进行标记,交给调用者解决

修饰符 返回类型 方法名()throws 异常类名1,异常类名2{

}

1.当方法中存在多个异常时,进行异常声明的时候无需考虑异常声明顺序(就算有异常间继承关系也不需要考虑,不会编译报错)


2.当方法中存在多个异常时,进行异常声明的时候可以简化只声明父类异常即可。


3、声明的异常类存在子父类继承关系

异常所在的类在父子类当中

1.如果异常在父类被重写的方法中,子类重写父类含有异常的方法时。无需考虑父类该方法的异常情况
2. 如果异常在子类重写的方法中,必须通过异常的捕获进行处理。不能用throws


public class Superclass {
    //实例变量
    int num=10;
    //实例方法
    public void method(){
        System.out.println("num="+num);
        System.out.println("this"+this);
    }

    public Superclass(){
        this.method();
    }
}


public class Subclass extends Superclass{
    //实例变量
    int num=20;
    //实例方法
    @Override
   //子类重写父类方法,有异常是必须try...catch,用声明异常会编译报错
    public void method() throws IOException{
        throw new IOException("自定义IO异常对象");
    }

    public Subclass(){
        super();
    }
}

4、try...catch

一个 try 块后面可以跟多个 catch 块,用来捕获不同类型的异常并做相应的处理,当 try 块中的某一行代码发生异常时,之后的代码就不再执行,而是会跳转到异常对应的 catch 块中执行。

try{
      //可能出现异常的代码
}catch( 异常类型 对象名){
    //出现该异常的解决方案[事务],从而保证程序继续运行
}catch(异常类型 对象名){
}

如果一个 try 块后面跟了多个与之关联的 catch 块,那么应该把特定的异常放在前面,通用型的异常放在后 面,不然编译器会提示错误。

代码中的异常类存在子父类继承关系
1、如果子父类解决方案不同,没有依赖关系,分别捕获用多个try。多次捕获,多次处理,对应      method02(1,1)。
2、如果子父类解决方案不同,有依赖关系。一次捕获,多次处理,此时俩个异常不可能同时处理了。分别处理时,需要将异常子类的处理方案写在最上面。对应method01(1,1)。
3、如果子父类解决方案相同,可以一次捕获,一次处理。

public class ExceptionDemo {
    public static void main(String[] args) {
        method01(1, 1);
        method02(1, 1);
    }

    public static void method01(int a, int b) {
        try {
            if (a == 1) {
                throw new FileNotFoundException("自定义文件找不到异常");
            }
            if (b == 1) {
                throw new IOException("自定义IO异常");
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void method02(int a, int b) {

        if (a == 1) {
            try {
                throw new FileNotFoundException("自定义文件找不到异常");
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }

        if (b == 1) {
            try {
                throw new IOException("自定义IO异常");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

5、finally

当我们在try语句块中打开了一些物理资源(磁盘文件/网络连接/数据库连接等),我们都得在使用完之后,最终关闭打开的资源。

为了保证发不发生异常都能执行一些代码,就会跟一个 finally 块。

使用finally,不管最后有没有报异常,最后都执行了关闭资源的操作

public class ExceptionDemo {
    public static void main(String[] args) {
        method01();
    }

    public static void method01() {
        Scanner sc = null;
        try {
            sc = new Scanner(System.in);
            System.out.println("请输入一个整数");
            int anInt = sc.nextInt();
            System.out.println("anInt=" + anInt);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (sc != null) {
                System.out.println("关闭资源");
                sc.close();
            }
        }
    }
}

启动main函数,程序等待输入一个数字,输入整数后如下,

程序结束运行

启动main函数,程序等待输入一个数字,输入非整数后如下,

程序结束运行


finally注意事项
1.在try里准备执行return时,突然jvm发现return是结束方法的意思,后续还有一个finally,会先执行finally,try里的return暂缓执行
2.在try,catch,finally里尽量避免使用return关键字。

即便是 try 块中执行了 return、break、continue 这些跳转语句,finally 块也会被执行

不走finally的情况:执行了 System. exit() 这行代码,退出程序。

System.exit() 和 return 语句不同,前者是用来退出程序的,后者只是回到了上一级方法调用。
public class ExceptionDemo {
    public static void main(String[] args) {
        int num = method01();
        System.out.println("num="+num);//num=10
    }

    public static int method01() {
        int a = 10;
        int b = 20;
        try{
            System.out.println("try");
            return a;  //已经决定return 10了,a在finally里再改也没用。
        }catch(Exception e){
            e.printStackTrace();
            return 1;
        }finally{
            a=a+b;
        }
    }
}


6、try-with-resources

要求JDK版本 >= 1.7:在 Java 7 之前,try–catch-finally 的确是确保资源会被及时关闭的最佳方法,无论程序是否会抛出异常。

在处理必须关闭的资源时, 始终有限考虑使用 try-with-resources,而不是 try–catch-finally。前者产生的代码更加简洁、清晰,产生的 异常信息也更靠谱。”

直接使用 try-with-resource 来处理是最佳方式,如果资源没有实现 AutoCloseable 接口,就在 finally 块关闭流

try-with-resources 语句中的资源只能是实现了java.lang.AutoCloseable接口的类实例,但是 Java SE 7 之後的所有实现了java.io.Closeable的类都实现了java.lang.AutoCloseable接口(该接口是在Java SE 7中才引入的),故都可以作为这里的资源

//源码
public interface Closeable extends AutoCloseable {
    void close() throws IOException;
}

try-with-resources 语句用法:在try关键字後面伴随一对小括号,并在括号中声明try块中要用到的资源。

用 Scanner 类来۾描控制台输入

public class ExceptionDemo {
    public static void main(String[] args) {
        method01();
    }

    public static void method01() {
        try (Scanner sc = new Scanner(System.in)) {
            System.out.println("请输入一个整数");
            int anInt = sc.nextInt();
            System.out.println("anInt=" + anInt);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

在退出try块(不论正常或异常退出)会自动关闭(自动调用close()),释放资源。

多个资源:通过使用分号分隔,可以在try-with-resources块中声明多个资源。

try (
    ZipFile zf = new ZipFile(zipFileName);
    BufferedWriter writer =
        java.nio.file.Files.newBufferedWriter(outputFilePath, charset)
    ) {
        // Enumerate each entry
    }

当从try块中(以正常或异常方式)退出时,在 try-with-resources 语句中所声明资源的关闭方法都会被自动调用,并且是按与资源声明序相反的顺序调用其close()方法的。

最後,try-with-resources 语句可以像Java SE 7 之前的普通try语句一样带有catch块和finally块,但在执行catch块(若有必要)和finally块中的代码前,会先关闭 try-with-resources 语句中声明的资源。

在生产环境中,应该使用日志系统来记录异常信息,例如 log4j、slf4j、logback 等。日志系统可以将异常信 息记录到文件或数据库中,而不会暴露敏感信息,也不会影响程序的性能和稳定性。同时,日志系统也提供 了更多的功能,如级别控制、滚动日志、邮件通知等。

有用的异常消息和堆栈跟踪非常重要,如果你的日志不能定位异常位置,那要日志有什么用呢?

// Log exception message and stack trace
LOGGER.debug("Error reading file", e);

7、自定义异常

1.自定义运行时异常:
        1.1自定义运行时异常类。让这个类继承RuntimeException
        1.2 根据实际需求生成合适的构造器,至少2个(无参,String参数)

如下是我之后在“尚医通-预约挂号”项目中用的自定义异常

@Data
@NoArgsConstructor
@AllArgsConstructor
public class YyghException extends RuntimeException {
    private String msg;
    private Integer code;
}

2.自定义编译时异常:
        2.1自定义编译时异常类。让这个类继承Exception
        2.2 根据实际需求生成合适的构造器,至少2个(无参,String参数)

自定义一个运行时异常

public class ZDYrunTimeException extends RuntimeException{

    public ZDYrunTimeException() {
    }

    public ZDYrunTimeException(String message) {
        super(message);//这行代码很关键。。。
    }
}

测试类:

public class ExceptionDemo {
    public static void main(String[] args) {
        try {
            throw new ZDYrunTimeException("我和你");
        }catch (ZDYrunTimeException zdYrunTimeException){
            zdYrunTimeException.printStackTrace();
            System.out.println(zdYrunTimeException.getMessage());
        }
    }
}

运行结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值