学习java第23天
2020/8/26
1.类加载
1.类加载器
- 当一个类第一次被使用时,会被加载到方法区,一个类只会被加载一次。
2.类的加载时机
- 创建类的实例。
- 调用类的静态变量,或者为静态变量赋值。
- 调用类的静态方法。
- 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象。
- 初始化某个类的子类。
- 直接使用java.exe命令来运行某个主类。
3.类加载器
-
类加载器就是把类加载到内存的工具。
-
启动类加载器(Bootstrap ClassLoader)
-
扩展类加载器(Extension ClassLoader) 在JDK9之后变成了平台类加载器[email protected]
-
应用程序类加载器(Application ClassLoader)
-
-
idea获取类加载器
【应用程序类加载器】 的父类是 【扩展类加载器】 的父类是 【启动类加载器】
public class Test02 { public static void main(String[] args) { //获取加载Test02这个类的类加载器 ClassLoader classLoader = Test02.class.getClassLoader(); System.out.println(classLoader); //[email protected] //获取它的父类加载器 ClassLoader parent = classLoader.getParent(); System.out.println(parent); //[email protected] //获取父类的父类加载器 ClassLoader parent1 = parent.getParent(); System.out.println(parent1); //null } }
2.反射
1.反射的概念
- 反射机制的作用是在程序的运行期间,通过类的字节码对象来获取类的成员并操作,(成员方法、成员变量、构造方法、注解)
2.获取字节码对象的三种方式
- 使用类名调用.class属性
- 使用对象调用getClass()方法
- 使用Class的静态方法forName()
public class Test01 {
public static void main(String[] args) throws ClassNotFoundException {
//1.使用类名调用.class属性
Class c1 = String.class;
Class c2 = Test01.class;
//2.使用对象调用getClass()方法
Class c3 = "abc".getClass();
//3.使用Class的静态方法forName()
Class c4 = Class.forName("java.lang.String");
System.out.println(c1 == c2); //false
System.out.println(c1 == c3); //true
System.out.println(c1 == c4); //true
}
}
3.反射操作构造方法
-
反射获取构造方法
- Class类的方法
- getConstructor(Class…c)可以获取某一个构造方法(公共的)
- getConstructors() 可以获取所有的构造方法(公共的)
- Class类的方法
-
反射执行构造方法
- Constructor类的方法
- newInstance(Object… obj) : 执行当前构造方法创建对象。 Object… obj 表示的是创建对象时的实际参数。
import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.Arrays; public class Test02 { public static void main(String[] args) throws Exception { //获取学生类的字节码对象 Class c = Student.class; //可以使用Class对象来获取类中的构造方法 //getConstructor(Class... c) 可以获取某一个构造方法(公共的) //获取空参构造 Constructor con = c.getConstructor(); //获取有参构造 Constructor con1 = c.getConstructor(String.class, int.class); //使用空参构造创建对象 //newInstance(Object... obj) : 执行当前构造方法创建对象。 Object obj1 = con.newInstance(); //向下转型 Student s1 = (Student) obj1; System.out.println(s1); //Student{name='null', age=0} //使用有参构造创建对象 Object obj2 = con1.newInstance("柳岩",38); System.out.println(obj2); //Student{name='柳岩', age=38} //getConstructors() 可以获取所有的构造方法(公共的) Constructor[] cons = c.getConstructors(); System.out.println(Arrays.toString(cons)); //字节码对象有快捷创建对象的方式,能够直接使用空参构造创建对象 //但是这个方法过时了,不建议使用了 Object o = c.newInstance(); System.out.println(o); } }
- Constructor类的方法
4.反射操作成员方法
-
反射获取成员方法
-
Class类的方法:
-
getMethod(String name, Class… c) :获取一个成员方法(公共的)
String name 表示方法名称
Class… c 表示的是方法的参数的类型
-
getMethods() : 获取类中的所有方法(公共的)
-
-
-
反射执行成员方法
-
Method类的方法:
-
Object invoke(Object obj , Object… o) :让方法执行
第一个参数表示执行的对象
第二个参数表示方法的实际参数
返回值表示方法的实际返回值
-
-
import java.lang.reflect.Method;
public class Test03 {
public static void main(String[] args) throws Exception {
//获取字节码对象
Class c = Class.forName("com.itheima_02.Student");
//getMethod(String name, Class... c) :获取一个成员方法(公共的)
//第一个参数:方法的方法名。 第二个参数:方法的参数类型
//获取睡觉方法
Method m1 = c.getMethod("sleep");
//获取吃饭方法
Method m2 = c.getMethod("eat", String.class);
//getMethods() : 获取类中的所有方法(公共的)
//包含了自己的方法和父类的方法
Method[] methods = c.getMethods();
//创建学生对象
Student s = new Student("柳岩",38);
//Object invoke(Object obj , Object... o) :让方法执行
//之前的调用:对象,方法,实参
//s.eat("米饭");
//反射的调用:
m2.invoke(s, "米饭");
//执行睡觉方法
m1.invoke(s);
}
}
5.暴力反射
-
之前的方法只能获取公共的成员,暴力反射的意思就是可以强制获取类中的所有成员。不管是不是public都可以获取到。用法就是在之前的方法中间加一个词Declared
getMethod() getDeclaredMethod()
getConstructor() getDeclaredConstructor()
等等每个方法都有。。。。但是我们一般不要使用,因为他打破了java的语法规范
import java.lang.reflect.Method; public class Test04 { public static void main(String[] args) throws Exception { //获取字节码对象 Class c = Class.forName("com.itheima_02.Student"); //暴力获取这个方法 Method m2 = c.getDeclaredMethod("eat", String.class); //设置强制访问 m2.setAccessible(true); //创建学生对象 Student s = new Student("柳岩",38); //反射的调用: m2.invoke(s, "米饭"); } }
6.反射操作成员变量
-
Class类中方法:
- getField(String name) : 获取一个成员变量。参数表示成员变量的名称
- 在类中成员变量都是私有的,建议不要暴力反射直接操作成员变量。
- 应该调用对应的公共的setget方法。
import java.lang.reflect.Field; public class Test05 { public static void main(String[] args) throws NoSuchFieldException { //获取字节码对象 Student s = new Student(); Class c = s.getClass(); //getField(String name) :获取成员变量 Field f1 = c.getDeclaredField("name"); System.out.println(f1); //接下来可以给他进行赋值,但是我们不往下写了,也不会使用这个办法。 } }
7.反射的作用案例演示
-
反射是java框架的灵魂。
-
案例演示
学习反射不是为了代码简单。为了更加灵活。
-
配置文件
cn=com.itheima02.Dog mn=eat
-
代码
public abstract class Animal { public abstract void sleep(); public abstract void eat(); } public class Cat extends Animal { @Override public void sleep() { System.out.println("猫咕噜咕噜地睡觉"); } @Override public void eat() { System.out.println("猫吃鱼"); } } public class Dog extends Animal { @Override public void sleep() { System.out.println("狗安静地睡觉"); } @Override public void eat() { System.out.println("狗爱吃骨头"); } } import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.Properties; public class Test { public static void main(String[] args) throws Exception { Properties p = new Properties(); p.load(new FileReader("day14\\config.properties")); String cn = p.getProperty("cn"); String mn = p.getProperty("mn"); Class c = Class.forName(cn); Constructor con = c.getConstructor(); Animal a = (Animal)con.newInstance(); Method m = c.getMethod(mn); m.invoke(a); } }
3.注解
1.概述
- 在java中,使用@来表示注解,注解也是类的一个组成部分。
2.注解的作用
-
生成帮助信息
-
编译检查
-
功能型
4.自定义注解
1.定义格式
public @interface 注解名{
属性
}
2.注解的属性
-
属性的格式
数据类型 属性名();
-
数据类型
基本数据类型 String Class 枚举 注解 以上类型的数组形式
-
示例
//名叫AAA的注解 public @interface AAA { //数据类型 属性名(); int age(); //基本类型 String name(); //String int[] num(); //数组类型 Class c(); //Class类型 BBB b(); //注解类型 }
3==.注意事项==
-
属性可以定义默认值,定义了默认值,就不会要求必须赋值了
int age() default 18; String name() default "柳岩"; int[] num() default {11,22};
-
如果注解中只有一个属性需要赋值,且属性名叫value,那么在使用注解时,可以省略键的部分
定义: String value(); 使用: @AAA("男")
-
数组形式的元素中如果只有一个元素,那么可以省略大括号
num是一个数组属性 @AAA(value = "男",num=11)
4.元注解
- 元注解就是给注解加的注解。
@Target
@Target
表示注解的作用位置
ElementType.FIELD 成员变量
ElementType.METHOD 成员方法
ElementType.TYPE 类
还有别的取值我们不用,如果不设置Target默认是在任何位置都能加
@Retention
@Retention
表示注解的存活阶段
RetentionPolicy.SOURCE 源码阶段
RetentionPolicy.CLASS 编译阶段
RetentionPolicy.RUNTIME 运行阶段
如果不写Retention默认会存活到CLASS
5.解析注解的内容
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface AAA {
String value();
int age() default 16;
}
import java.lang.annotation.Annotation;
@AAA("柳岩")
public class Test01 {
public static void main(String[] args) {
Class c = Test01.class;
Annotation anno = c.getAnnotation(AAA.class);
AAA a = (AAA)anno;
String value = a.value();
System.out.println(value);
int age = a.age();
System.out.println(age);
}
}
6.案例完成测试框架
import java.util.HashSet;
import java.util.Set;
//假设是我们开发的一个程序,一个程序中有很多代码有很多方法,我们想要测试方法有没有异常的产生
public class MyQQ {
public void method(){
int[] arr = new int[4];
arr[10] = 5;
}
public void method01(){
String s = "";
int length = s.length();
}
public void method02(){
Set<String> set = new HashSet<>();
set.add("张三");
set.add("老王");
set.add("赵四");
for (String s : set) {
if(s.equals("老王")){
set.remove(s);
}
}
}
public void method03(){
String s = null;
int length = s.length();
}
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Check {
}
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Method;
public class Test01 {
public static void main(String[] args) throws IOException {
BufferedWriter bw = new BufferedWriter(new FileWriter("day14\\bug.txt"));
MyQQ qq = new MyQQ();
Class c = qq.getClass();
Method[] methods = c.getMethods();
int count = 0;
for (Method method : methods) {
if (method.isAnnotationPresent(Check.class)) {
try {
method.invoke(qq);
} catch (Exception e) {
//e.printStackTrace();
bw.write("在" + method.getName() + "方法中:出现了" + e.getCause() + "异常");
bw.newLine();
bw.write("-------------------");
bw.newLine();
}
}
}
bw.write("本次测试发现了"+count+"个异常");
bw.close();
}
}
5.动态代理
1.作用
- 动态代理可以对一个类的方法进行动态的增强(改变),利用反射机制在程序的运行期间对方法进行操作。
2.使用前提
- 类必须要实现接口
3.ArrayList使用工具类的演示
//集合
List<String> list = new ArrayList<>();
list.add("柳岩");
list.add("崔航");
list.add("奥利给");
//工具类
//返回的是list的代理对象
//代理对象和被代理对象相当于真假对象,这两个对象非常相似,用list2去代替list
//孙悟空list 六耳猕猴list2
//List<String> list2 = Collections.unmodifiableList(list);
4.案例:不允许集合增删改
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
@SuppressWarnings("All")
public class Test01 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("柳岩");
list.add("大鹏");
list.add("岳云鹏");
List<String> list2 = method(list);
boolean b = list2.add("石原里美");
System.out.println(b);
}
private static List<String> method(ArrayList<String> list) {
List<String>obj = (List<String>)Proxy.newProxyInstance(list.getClass().getClassLoader(), list.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String name = method.getName();
if (name.equals("add") || name.equals("set") || name.equals("remove")) {
throw new RuntimeException("不允许调用增删改方法");
} else {
Object obj = method.invoke(list, args);
return obj;
}
}
});
return obj;
}
5.案例:集合只允许添加四个字的字符串
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
@SuppressWarnings("All")
public class Test02 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("孙悟空");
list.add("猪八戒");
List<String> list2 = method(list);
list2.add("沙僧");
}
private static List<String> method(ArrayList<String> list) {
List<String> o = (List<String>) Proxy.newProxyInstance(list.getClass().getClassLoader(), list.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("add")) {
String s = (String) args[0];
if (s.length() != 4) {
throw new RuntimeException("只允许添加长度为4的字符串!!!");
}
}
Object obj = method.invoke(list, args);
return obj;
}
});
return o;
}
}
6.Lombok插件
import lombok.*;
@Data
//@Setter
//@Getter
//@ToString
//@EqualsAndHashCode
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private String name;
private int age;
}
上一篇: Java学习第23天