枚举类
1、命名规范
枚举是jdk5.0的特性,用于创建固定数量对象的简化方式,如月份,星期,季节类。使用关键字enum定义枚举类型
命名规范:
- 枚举类名带上Enum后缀
- 枚举成员名称需要全部大写,单词间用下划线隔开
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 } }
枚举类的构造方法被默认强制私有
- 如果枚举类没有任何的构造器时,jvm会自动提供一个private的无参构造器,反之不提供
- 枚举类中的构造器权限访问级别只能是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类中常用的方法:
- Throwable(String message) 构造带指定详细消息的新 throwable。
- printStackTrace() //打印Throwable对象的详细信息,红色字体
- 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()); } } }
运行结果: