一、异常处理

1.异常介绍

异常处理、反射、注解、泛型_异常 反射

Throwable是异常体系的根,它继承自Object。Throwable有两个体系:Error和Exception

Error(错误)是系统中的错误,程序员是不能改变的和处理的,是在程序编译时出现的错误,只能通过修改程序才能修正。一般是指与虚拟机相关的问题,如系统崩溃,虚拟机错误,内存空间不足,方法调用栈溢等。对于这类错误的导致的应用程序中断,仅靠程序本身无法恢复和和预防,遇到这样的错误,建议让程序终止

OutOfMemoryError:内存耗尽
NoClassDefFoundError:无法加载某个Class
StackOverflowError:栈溢出
  • 1.
  • 2.
  • 3.

Exception(异常)表示程序可以处理的异常,可以捕获且可能恢复。遇到这类异常,应该尽可能处理异常,使程序恢复运行,而不应该随意终止异常(程序逻辑编写不对造成的,应该修复程序本身)

ArrayIndexOutOfBoundsException  数组越界时抛出的异常,下标超出了数组的范围

ClassCastException          试图将对象强制转换为不是实例的子类时,抛出该异常

IllegalArgumentException    抛出的异常表明向方法传递了一个不合法或不正确的参数

NoSuchElementException      表明枚举中没有更多的元素

NullPointerException        当应用程序试图在需要对象的地方使用 null 时,抛出该异常
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

2.捕获异常

捕获异常使用try...catch语句,把可能发生异常的代码放到try {...}中,然后使用catch捕获对应的Exception及其子类:

try{
            int[] a = {1,2};
            System.out.println(a[2]);
        }catch (ArrayIndexOutOfBoundsException e ){
            e.printStackTrace();
            System.out.println("数组越界");
        }

		try{
            int[] a = {1,2};
            System.out.println(a[2]);
        }catch (NullPointerException e ){
            e.printStackTrace();
            System.out.println("数组越界");
        }


	try{
            int[] a = {1,2};
            System.out.println(a[2]);
        }catch (Exception e ){
            e.printStackTrace();
            System.out.println("数组越界");
        }
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.

我们通常捕获所有异常的父类Exception 在能确定异常的情况下使用多 catch 块来捕获异常来明确异常信息

try{
            int[] a = {1,2};
            System.out.println(a[2]);
        }catch (ArrayIndexOutOfBoundsException e ){
            e.printStackTrace();
            System.out.println("数组越界");
        }catch (NullPointerException e ){
            e.printStackTrace();
            System.out.println("空指针异常");
        }catch (NumberFormatException  e ){
            e.printStackTrace();
            System.out.println("数值转换异常");
        }catch (Exception e ){
            e.printStackTrace();
            System.out.println("异常");
        }
        由于只有一个异常被捕获 所以多个catch语句只有一个能被执行(父级异常写在下面)
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.

finally块用来保证一些代码必须执行

try{
            int[] a = {1,2};
            System.out.println(a[2]);
        }catch (Exception e ){
            e.printStackTrace();
            System.out.println("异常");
        }finally{
			System.out.println("结束");
		}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

3.抛出异常

1.创建某个Exception的实例;

NullPointerException e = new NullPointerException();
        throw e;
  • 1.
  • 2.

2.用throw语句抛出;

throw new NumberFormatException("null");
  • 1.

4.自定义异常

通常会创建一个类并集成BaseException,然后派生出各种业务类型的异常。 BaseException继承自RuntimeException,继承多个构造方法,这样抛出异常的时候,就可以选择合适的构造方法

public class Demo extends BaseException {

    public Demo (String module, String code, Object[] args, String defaultMessage) {
        super(module, code, args, defaultMessage);
    }

    public Demo (String module, String code, Object[] args) {
        super(module, code, args);
    }

    public Demo (String module, String defaultMessage) {
        super(module, defaultMessage);
    }

    public Demo (String code, Object[] args) {
        super(code, args);
    }

    public Demo (String defaultMessage) {
        super(defaultMessage);
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.

BaseException需要从一个适合的Exception派生,通常建议从RuntimeException派生

5.断言

主要使用在代码开发和测试时期,用于对某些关键数据的判断,如果这个关键数据不是你程序所预期的数据,程序就抛出AssertionError异常提出警告或退出(默认关闭)

public class AssertionDemo {  
    //这个成员变量的值可以变,但最终必须还是回到原值5  
    static int i = 5;  
    public static void main(String[] args) {  
        assert i==6;  
        System.out.println("如果断言正常,我就被打印");  
    }  
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.

6.日志

在JDK 1.3及以前,Java打日志依赖System.out.println(), System.err.println()或者Exception的printStackTrace()方法来实现,现在常用的日志框架有JDK Logging、Log4j、SLF4J、Logback

通常一个类只有一个 LOG 对象,如果有父类可以将 LOG 定义在父类中

使用JDK Log进行日志打印操作如下

public class Main {
    public static void main(String[] args) {
    	Logger log= Logger.getLogger(this.getName());
        log.info("start...");
        log.fine("end.");
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

使用Java标准库内置的Logging一旦开始运行,就无法修改配置所以Java标准库内置的Logging使用并不是非常广泛

log4j

Log4j的配置一般放在文件里(log4j.properties、log4j.xml) 由三个重要的组成构成:日志记录器(Loggers),输出端(Appenders)和日志格式化器(Layout)

1.Logger:控制要启用或禁用哪些日志记录语句,并对日志信息进行级别限制

2.Appenders : 指定了日志将打印到控制台还是文件中 3.Layout : 控制日志信息的显示格式

异常处理、反射、注解、泛型_System_02

### 设置###
log4j.rootLogger = debug,stdout,D,E

### 输出信息到控制抬 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n

### 输出DEBUG 级别以上的日志到=E://logs/error.log ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = E://logs/log.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG 
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.

Log4j中将要输出的Log信息定义了5种级别,依次为DEBUG、INFO、WARN、ERROR和FATAL

日志级别

描述

OFF 关闭

最高级别 不输出日志

TRACE 跟踪

输出更细致的程序运行轨迹

DEBUG 调试

输出更细致的对调试应用有用的信息

INFO 信息

输出应用运行过程的详细信息

WARN 警告

输出可能潜在的危险状况

ERROR 错误

输出错误,但应用还能继续运行

FATAL 致命

输出非常严重的可能会导致应用程序终止的错误

ALL 所有

输出所有级别信息

private static final Logger logger= LoggerFactory.getLogger(this.getClass());
		 // 记录debug级别的信息    
        logger.debug("This is debug message.");    
        // 记录info级别的信息    
        logger.info("This is info message.");    
        // 记录error级别的信息    
        logger.error("This is error message.");
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

Slf4j、Logback

Slf4j 也是现在主流的日志门面框架,使用 Slf4j 可以很灵活的使用占位符进行参数占位,简化代码,拥有更好的可读性. Logback 是 Slf4j 的原生实现框架,同样也是出自 Log4j 一个人之手,但拥有比 log4j 更多的优点、特性和更做强的性能,现在基本都用来代替 log4j 成为主流

SLF4J的日志采用接口传入的形式 而且增加了可以带占位符的字符串写法,用后面的变量自动替换占位符

int score = 99;
p.setScore(score);
logger.info("Set score {} for Person {} ok.", score, p.getName());
  • 1.
  • 2.
  • 3.

二、反射

1.概念

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性,这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

2.Class

创建的每一个类也都是对象,即类本身是java.lang.Class类的实例对象,这个实例对象称之为类对象,也就是Class对象,jvm中有很多的实例,每个类都有唯一的Class对象.

一个类被加载到内存并供我们使用需要经历三个阶段 加载、链接、初始化.

所有的类都是在对其第一次使用时,动态加载到JVM中的(懒加载),因此java程序程序在它开始运行之前并非被完全加载,其各个类都是在必需时才加载的,在类加载阶段,类加载器首先检查这个类的Class对象是否已经被加载如果尚未加载,默认的类加载器就会根据类的全限定名查找.class文件

获取class的方式有三种

1.Class.forName(“类的全限定名”) 2.实例对象.getClass() 3.类名.class (类字面常量)

package com.cry;
class Cat {
    static {
        System.out.println("Loading Cat");
    }
}
public class Test {
    public static void main(String[] args) throws ClassNotFoundException {
        System.out.println("inside main");
        Class c1 = Cat.class;
        Class c2= Class.forName("com.cry.Cat");
        Class c3=new Cat().getClass();
        System.out.println(c1==c2);
        System.out.println(c2==c3);
        System.out.println("finish main");
    }
}
/* Output:
inside main
-------
Loading Cat
true
true
finish main
 */
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.

获取数据

class SysUsers{
    private String userName;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public void ToString(String data){
        System.out.println(data);
    }
}
public void Test() throws Exception {
        SysUser sysUser = new SysUser();
        sysUser.setUserName("demo");
        Class cls = sysUser.getClass();
        Field field = cls.getField("userName"); //包括父类
        System.out.println(field.getName());//返回字段名称
        System.out.println(field.getType());//返回字段类型
        System.out.println(field.getModifiers()); //返回字段的修饰符,它是一个int
        Field field_02 = cls.getDeclaredField("userName"); //不包括父类,但可获取私有字段
        Field[] fields = cls.getFields();
        Field[] fields_02 = cls.getDeclaredFields();
        //field.setAccessible(true); //设置允许访问私有字段权限 可能会失败
        System.out.println(field_02.get(cls));
        field_02.set(field_02,"demo02");
        System.out.println(field_02.get(cls));

    }
/* Output:
		userName
		class java.lang.String
		2
		demo
		demo2
 */
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.

setAccessible(true)可能会失败。如果JVM运行期存在SecurityManager,那么它会根据规则进行检查,有可能阻止setAccessible(true)。例如,某个SecurityManager可能不允许对java和javax开头的package的类调用setAccessible(true),这样可以保证JVM核心库的安全

方法

public void Test02() throws Exception {
        SysUsers sysUser = new SysUsers();
        sysUser.setUserName("demo");
        Class cls = SysUsers.class;
        Method method = cls.getMethod("ToString",String.class); //包括父类
        System.out.println(method.getName()); //返回方法名称
        System.out.println(method.getReturnType());//返回返回值类型
        System.out.println(method.getParameterTypes()[0]);//返回方法的参数类型
        System.out.println(method.getModifiers());//返回方法的修饰符,它是一个int
        Method method_02 = cls.getDeclaredMethod("ToString",String.class); //不包括父类,但可获取私有方法
        Method[] methods = cls.getMethods();
        Method[] methods_02 = cls.getDeclaredMethods();
        method_02.setAccessible(true); //设置允许访问私有方法权限 可能会失败
        method.invoke(cls.newInstance(),"demo");

 		//静态方法 获取Integer.parseInt(String)方法,参数为String:
        Method m = Integer.class.getMethod("parseInt", String.class);
        // 调用该静态方法并获取结果:
        Integer n = (Integer) m.invoke(null, "12345");
    }
/* Output:
		ToString
		void
		class java.lang.String
		1
		demo
 */
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
Class i = Integer.class;
        Class n = i.getSuperclass();
        System.out.println(n);
        Class o = n.getSuperclass();
        System.out.println(o);
        System.out.println(o.getSuperclass());
/* Output:
		class java.lang.Number 
		class java.lang.Object 
		null 
 */
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.

动态代理

 JDK动态代理就是要生成一个包装类对象,由于代理的对象是动态的,所以叫动态代理。由于我们需要增强,这个增强是需要留给开发人员开发代码的,因此代理类不能直接包含被代理对象,而是一个InvocationHandler,该InvocationHandler包含被代理对象,并负责分发请求给被代理对象,分发前后均可以做增强