反射
反射是如何获取类的信息、操作它们
1、反射第一步:加载类,获取类的字节码:class对象
2、获取类的构造器:Constructor
3、获取类的成员变量:Field对象
4、获取类的成员变量:Method对象
反射第一步:加载类,获取类的字节码:class对象 Student.java————>Student.class————>字节码文件——(加载到内存中)——>内存中(Student.class)
1 2 3 4 5 6 7 8 9 10 11 Class alunbarClass1 = Class.forName("cn.javaguide.TargetObject" );ClassLoader.getSystemClassLoader().loadClass("cn.javaguide.TargetObject" ); Class c1 = TargetObject.class;TargetObject o = new TargetObject ();Class alunbarClass2 = o.getClass();
举例: 定义cat类 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 40 41 package com.li.d2_reflect;public class Cat { public static int a; public static final String COUNTRY = "中国" ; private String name; private int age; public Cat () { System.out.println("无参数构造器执行了~~" ); } public Cat (String name, int age) { System.out.println("无有参数构造器执行了~~" ); this .name = name; this .age = age; } private void run () { System.out.println("猫跑的贼快~" ); } private String eat () { System.out.println("猫爱吃猫粮~" ); } private String eat (String name) { return "猫最爱吃:" + name; } public String getName () { return name; } public void setName (String name) { this .name = name; } public String getAge () { return age; } public void setName (int age) { this .age = age; } @Override public String tostring () {return "Cat{" + "name='" + name + '\'' + " , age=" + age +';' ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 public class Test1Class { public static void main (String[] args) throws Exception { Class c1 = Student.class; System.out.println(c1.getName()); System.out.printn(c1.getSimpleName()); Class c2 = Class.forName("com.itheima.d2_reflect.Student" );System.out.println(c1 == c2); Student s = new Student (); Class c3 = s.getClass(); System.out.println(c3 == c2); } }
反射第二步:获取类的构造器、并对其进行操作 Class提供了从类中获取构造器的方法 1 2 3 4 5 6 7 8 9 10 11 Constructor<?>[] getConstructors(); Constructor<?>[] getDeclaredConstructors(); Constructor<T> getConstructor (Class<?>... parameterTypes) ; Constructor<T> getDeclaredConstructor (Class<?>..,parameterTypes) ;
例子: 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 public class Test2Constructor { @Test public void testGetConstructors () { Class c = Cat.class; Constructor[] constructorsPublicType = c.getConstructors(); Constructor[] constructorsAllType = c.getDeclaredConstructors(); for (Constructor constructor : constructorsPublicType) { System.out.println(constructor.getName() + "--->" + constructor.getParameterCount()); } } @Test public void testGetConstructor () throws Exception { Class c = Cat.class Constructor constructorsPublicType = c.getConstructor(); Constructor constructorsAllType = c.getDeclaredConstructor(); System.out.println(constructor.getName() + "--->" + constructor.getParameterCount()); Constructor constructor1 = c.getDeclaredConstructor(String.class, int .class); System.out.printIn(constructor1.getName() +"--->" + constructor1.getParameterCount()); } com.li.d2_reflect.Cat--->0 com.li.d2_reflect.Cat--->2 com.li.da_reflect.Cat--->0 com.li.d2_reflect.Cat--->2
获取类构造器的作用: 依然是初始化对象返回 1 2 3 4 T newInstance (Object... initargs) public void setAccessible (boolean flag)
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 public class Test2Constructor { @Test public void testGetConstructor () throws Exception { Class c = Cat.class Constructor constructorsPublicType = c.getConstructor(); Constructor constructorsAllType = c.getDeclaredConstructor(); System.out.println(constructor.getName() + "--->" + constructor.getParameterCount()); constructorsPublicType.setAccessible(true ); Cat cat = (Cat) constructorsPublicType.newInstance(); System.out.println(cat); Constructor constructor1 = c.getDeclaredConstructor(String.class, int .class); System.out.printIn(constructor1.getName() +"--->" + constructor1.getParameterCount()); constructor1.setAccessible(true ); Cat cat2 = (Cat) constructor2.newInstance( ...initargs:"叮当猫" ,3 ); System.out.printn(cat2); } com.li.d2_reflect.Cat--->0 无参数构造器执行了~~ Cat{name='null' ,age=0 } com.li.d2_reflect.Cat--->2 有参数构造器执行了~~ Cat{name='叮当猫工 age=3}
反射第三步:获取类的成员变量 Class提供了从类中获取成员变量的方法 1 2 3 4 5 6 7 8 public Field[] getFields()public Field[] getDeclaredFields()public Field getField (String name) public Field getDeclaredField (String name)
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 40 41 42 43 44 45 46 47 48 public class Test3Field { @Test public void testGetFields () { Class c = Cat.class; Field[] fields = c.getDeclaredFields(); for (Field field : fields) { System.out.println(field.getName()+"---> " + field.getType()); } Field fName = c.getDeclaredField( name: "name" ); System.out.println(fName.getName() + "--->" + fName.getType()); Field fAge = c.getDeclaredField( name: "age" ); System.out.println(fAge.getName() + "--->" + fAge.getType()); Cat cat = new Cat (); fName.setAccessible(true ); fName.set(cat,"卡菲猎" ); System.out.printn(cat): String name = (String) fName.get(cat); System.out.println(name); } a---> int COUNTRY---> class java .lang.string name---> class java .lang.String age---> int name---> class java .lang.String age---> int 无参数构造器执行了~~ Cat{name='卡菲猫' ,age=0 } 卡菲猫
获取到成员变量的作用: 依然是赋值、取值 1 2 3 4 5 6 void set (Object obj,object value) ;Object get (Object obj) ; public void setAccessible (boolean flag) ;
反射第四步:获取类的成员方法 1 2 3 4 5 6 7 8 Method[] getMethods() Method[] getDeclaredMethods() Method[] getMethod(String name,Class<?>... parameterTypes) Method[] getDeclaredMtthod(String name,Class<?>...parameterTypes)
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 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 public class Test3Method { @Test public void testGetMethods () { Class c = Cat.class; Method[] methods = c.getDeclaredMethods(); for (Method method : methods){ System.out.println(method.getName() + "--->" + method.getParameterCount() + "--->" + method.getReturnType()); } Method run = c.getDeclaredMethod( name:"run" ); System.out.printn(run.getName() + "--->" + run.getParameterCount() + "--->" + run .getReturnType()); Method eat = c.getDeclaredMethod( name: "eat" , String.class); System.out.println(eat.getName() + "--->" + eat.getParameterCount() + "--->" + eat.getReturnType()); Cat cat = new Cat (); run.setAccessible(true ); object .rs = run.invoke(cat); System.out.printin(rs); eat.setAccessible(true ); String rs2 = (String) eat.invoke(cat,...args:"鱼儿" ); System.out.printin(rs2); } } getName--->0 --->class java .lang.String run--->0 ---->void toString--->0 ---->class java .lang.String setName--->1 ---->voideat--->1 ---->class java .lang.String eat--->0 ---->void getAge--->0 ---->int setAge--->1 ---->void run--->0 ---->void eat--->0 ---->void 猫跑的贼快~ null 猫最爱吃:鱼
成员方法的作用:依然是执行 1 2 3 4 public object invoke (Object obj,Object... args) public void setAccessible (boolean flag)
注解 1 2 3 4 public @interface 注解名称{ public 属性类型 属性名() default 默认值; }
例: 1 2 3 4 5 public @interface MyTest{ String aaa () ; boolean bbb () default true ; String[] ccc(); }
1 2 3 4 public @interface MyTest{ String value () ; int age () default 23 ; }
1 2 3 4 5 6 7 @MyTest1(aaa="牛魔王",ccc={"HTML","Java"}) @MyTest2(value = "孙悟空",age=1000) public class AnnotationTest1 { @MyTest1(aaa="铁扇公主",bbb=false,ccc={"Python","前端","Java"}) public void test1 () { } }
经过编译和反编译后,可以看出注解的源码
1 2 3 4 5 6 7 8 9 public interface MyTest1 extends Annotation { public abstract String aaa () ; public abstract boolean bbb () : public abstract String[] ccc(); } @MyTest1(aaa="牛魔王",ccc={"HTML","Java"})
元注解 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD) )public @interface Test {} @Target @Target(ElementType.TYPE) 1. TYPE,类,接口2. FIELD,成员变量3. METHOD,成员方法4. PARAMETER,方法参数5. CONSTRUCTOR,构造器6. LOCAL VARIABLE,局部变量 @Retention @Retention(RetentionPolicy.RUNTIME) 1. SOURCE 2. CLASS(默认值) 3. RUNTIME (开发常用)
解析注解例子: 1 2 3 4 AnnotatedElement接口提供了解折注解的方法: public Annotation[] getDeclaredAnnotations() public T getDeclaredAnnotation (class<T> annotationClass) public boolean isAnnotationPresent (Class<Annotation> annotationClass)
1 2 3 4 5 6 7 8 @Target({ElementType.TYPE,ELementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface MyTest4 { String value () ; double aaa () ; default 10 :String[] bbb(); }
1 2 3 4 5 6 7 @MyTest4(value ="蜘蛛精",aaa=99.5,bbb = {"至尊宝”,"黑马"}) public class Demo {@MyTest4(value ="孙悟空",aaa=199.9,bbb = {"紫霞","牛夫人"}) public void test1 () { } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class AnnotationTest3 { @Test public void parseclass () { Class c = Demo .class; if (c.isAnnotationPresent(MyTest4.class)){ MyTest4 myTest4 = (MyTest4) c.getDeclaredAnnotation(MyTest4.class); System.out.printIn(myTest4.value()); System.out.printn(myTest4.aaa()); System.out.println(Arrays.toString(myTest4.bbb())); } } } 蜘蛛精 99.5 [至尊宝,黑马]
注解的应用 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 public class AnnotationTest { @MyTest public void test1 () { System.out.println("===test1===" ); } public void test2 () { System.out.println("===test2===" ); } @MyTest public void test3 () { System.out.println("===test3===" ); } public void test4 () { System.out.println("===test4===" ); } public static void main (String[] args) { AnnotationTest t = new AnnotationTest ; Class c = AnnotationTest.class; Method[] methods = c.getDeclaredMethods(); for (Method method : methods){ if (method.isAnnotationPresent(MyTest.class)){ method.invoke(t); } } } }
动态代理 异常 认识异常 接下来,我们学习一下异常,学习异常有利于我们处理程序中可能出现的问题。我先带着同学们认识一下,什么是异常?
我们阅读下面的代码,通过这段代码来认识异常。 我们调用一个方法时,经常一部小心就出异常了,然后在控制台打印一些异常信息。其实打印的这些异常信息,就叫做异常。
那肯定有同学就纳闷了,我写代码天天出异常,我知道这是异常啊!我们这里学习异常,其实是为了告诉你异常是怎么产生的?只有你知道异常是如何产生的,才能避免出现异常。以及产生异常之后如何处理。
因为写代码时经常会出现问题,Java的设计者们早就为我们写好了很多个异常类,来描述不同场景下的问题。而有些类是有共性的所以就有了异常的继承体系
先来演示一个运行时异常产生
1 2 3 int [] arr = {11 ,22 ,33 };System.out.println(arr[5 ]);
下图是API中对ArrayIndexOutOfBoundsExcpetion类的继承体系,以及告诉我们它在什么情况下产生。
再来演示一个编译时异常
我们在调用SimpleDateFormat对象的parse方法时,要求传递的参数必须和指定的日期格式一致,否则就会出现异常。 Java比较贴心,它为了更加强烈的提醒方法的调用者,设计了编译时异常,它把异常的提醒提前了,你调用方法是否真的有问题,只要可能有问题就给你报出异常提示(红色波浪线)。
编译时异常的目的:意思就是告诉你,你小子注意了!!,这里小心点容易出错,仔细检查一下
有人说,我检查过了,我确认我的代码没问题,为了让它不报错,继续将代码写下去。我们这里有两种解决方案。
第一种:使用throws在方法上声明,意思就是告诉下一个调用者,这里面可能有异常啊,你调用时注意一下。
1 2 3 4 5 6 7 8 9 10 public class ExceptionTest1 { public static void main (String[] args) throws ParseException{ SimpleDateFormat sdf = new SimpleDateFormat ("yyyy-MM-dd HH:mm:ss" ); Date d = sdf.parse("2028-11-11 10:24" ); System.out.println(d); } }
第二种:使用try…catch语句块异常进行处理。
1 2 3 4 5 6 7 8 9 10 11 public class ExceptionTest1 { public static void main (String[] args) throws ParseException{ try { SimpleDateFormat sdf = new SimpleDateFormat ("yyyy-MM-dd HH:mm:ss" ); Date d = sdf.parse("2028-11-11 10:24" ); System.out.println(d); } catch (ParseException e) { e.printStackTrace(); } } }
好了,关于什么是异常,我们就先认识到这里。
1.2 自定义异常 同学们经过刚才的学习已经认识了什么是异常了,但是无法为这个世界上的全部问题都提供异常类,如果企业自己的某种问题,想通过异常来表示,那就需要自己来定义异常类了。
我们通过一个实际场景,来给大家演示自定义异常。
需求:写一个saveAge(int age)方法,在方法中对参数age进行判断,如果age<0或者>=150就认为年龄不合法,如果年龄不合法,就给调用者抛出一个年龄非法异常。0或者>
分析:Java的API中是没有年龄非常这个异常的,所以我们可以自定义一个异常类,用来表示年龄非法异常,然后再方法中抛出自定义异常即可。
先写一个异常类AgeIllegalException(这是自己取的名字,名字取得很奈斯),继承
1 2 3 4 5 6 7 8 9 public class AgeIllegalException extends Exception { public AgeIllegalException () { } public AgeIllegalException (String message) { super (message); } }
再写一个测试类,在测试类中定义一个saveAge(int age)方法,对age判断如果年龄不在0~150之间,就抛出一个AgeIllegalException异常对象给调用者。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class ExceptionTest2 { public static void main (String[] args) { try { saveAge2(225 ); System.out.println("saveAge2底层执行是成功的!" ); } catch (AgeIllegalException e) { e.printStackTrace(); System.out.println("saveAge2底层执行是出现bug的!" ); } } public static void saveAge (int age) { if (age > 0 && age < 150 ){ System.out.println("年龄被成功保存: " + age); }else { throw new AgeIllegalRuntimeException ("/age is illegal, your age is " + age); } } }
注意咯,自定义异常可能是编译时异常,也可以是运行时异常
1 2 3 4 5 1. 如果自定义异常类继承Excpetion,则是编译时异常。 特点:方法中抛出的是编译时异常,必须在方法上使用throws 声明,强制调用者处理。 2. 如果自定义异常类继承RuntimeException,则运行时异常。 特点:方法中抛出的是运行时异常,不需要在方法上用throws 声明。
1.3 异常处理 同学们,通过前面两小节的学习,我们已经认识了什么是异常,以及异常的产生过程。接下来就需要告诉同学们,出现异常该如何处理了。
比如有如下的场景:A调用用B,B调用C;C中有异常产生抛给B,B中有异常产生又抛给A;异常到了A这里就不建议再抛出了,因为最终抛出被JVM处理程序就会异常终止,并且给用户看异常信息,用户也看不懂,体验很不好。
此时比较好的做法就是:1.将异常捕获,将比较友好的信息显示给用户看;2.尝试重新执行,看是是否能修复这个问题。
我们看一个代码,main方法调用test1方法,test1方法调用test2方法,test1和test2方法中多有扔异常。
第一种处理方式是,在main方法中对异常进行try…catch捕获处理了,给出友好提示。
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 public class ExceptionTest3 { public static void main (String[] args) { try { test1(); } catch (FileNotFoundException e) { System.out.println("您要找的文件不存在!!" ); e.printStackTrace(); } catch (ParseException e) { System.out.println("您要解析的时间有问题了!" ); e.printStackTrace(); } } public static void test1 () throws FileNotFoundException, ParseException { SimpleDateFormat sdf = new SimpleDateFormat ("yyyy-MM-dd HH:mm:ss" ); Date d = sdf.parse("2028-11-11 10:24:11" ); System.out.println(d); test2(); } public static void test2 () throws FileNotFoundException { InputStream is = new FileInputStream ("D:/meinv.png" ); } }
第二种处理方式是:在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 26 27 28 29 30 public class ExceptionTest4 { public static void main (String[] args) { while (true ) { try { System.out.println(getMoney()); break ; } catch (Exception e) { System.out.println("请您输入合法的数字!!" ); } } } public static double getMoney () { Scanner sc = new Scanner (System.in); while (true ) { System.out.println("请您输入合适的价格:" ); double money = sc.nextDouble(); if (money >= 0 ){ return money; }else { System.out.println("您输入的价格是不合适的!" ); } } } }