Java学习笔记_16

来源:互联网 发布:pro tools 12.8.2 mac 编辑:程序博客网 时间:2024/06/03 15:14
一::类加载器(了解)
(1)什么是类的加载
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,
初始化三步来实现对这个类进行初始化。

一个类在加载过程中的三部曲:
1.加载 
就是指将class文件读入内存,并为之创建一个Class对象. 
任何类被使用时系统都会建立一个Class对象。

2.连接
验证 是否有正确的内部结构,并和其他类协调一致
准备 负责为类的静态成员分配内存,并设置默认初始化值
解析 将类的二进制数据中的符号引用替换为直接引用

3.初始化 就是我们以前讲过的初始化步骤


(2)类的加载时机(掌握)
2.1 创建类的实例
2.2 访问类的静态变量,或者为静态变量赋值
2.3 调用类的静态方法
2.4 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
2.5 初始化某个类的子类
2.6 直接使用java.exe命令来运行某个主类



(3)加载器分类
3.1 类加载起的作用?
负责将.class文件加载到内在中,并为之生成对应的Class对象。

3.2 类加载器的分类
Bootstrap ClassLoader 根类加载器
也被称为引导类加载器,负责Java核心类的加载
比如System,String等。在JDK中JRE的lib目录下rt.jar文件中

Extension ClassLoader 扩展类加载器
负责JRE的扩展目录中jar包的加载。
在JDK中JRE的lib目录下ext目录

Sysetm ClassLoader 系统类加载器

负责在JVM启动时加载来自java命令的class文件


二、反射

什么是反射:


Student.java--Student.class(字节码文件)--看成一个对象,这个对象就叫字节码文件对象--对应的类Class

(1)获取字节码文件对象的三种方式:
A:Object类的getClass()方法
B:数据类型的静态class属性
C:Class类的静态方法forName()
注意:
在平常写案例的时候,我们直接使用第二种最方便。
   但是实际开发中,我们一般用的都是第三种。是因为第三种接收的是一个字符串类型的参数,
   在开发中我们可以将这个字符串存储到配置文件中,让java文件实现一个动态的读取。
案例:使用上述三种方式获取类的Class对象

public class ReflectDemo {public static void main(String[] args) throws ClassNotFoundException {//获取Person类对应的Class字节码文件对象//A:Object类的getClass()方法Person p1 = new Person();Person p2 = new Person();Class c1 = p1.getClass();Class c2 = p2.getClass();System.out.println(p1==p2);//falseSystem.out.println(c1==c2);//true//每一个类会对应一个字节码文件对象,而这个字节码文件对象就是这个类的原型,每一个类有且仅有一个字节码文件对象System.out.println("-------------");//B:数据类型的静态class属性Class c3 = Person.class;System.out.println(c2==c3);System.out.println("---------------");//C:Class类的静态方法forName()//public static Class<?> forName(String className),在这里所说的类名是全类名(带包名的类名)//Class c4 = Class.forName("Person");//java.lang.ClassNotFoundException: PersonClass c4 = Class.forName("com.edu_01.Person");System.out.println(c3==c4);}}

(2)反射的使用步骤
 Class:
  成员变量 Field
  构造方法 Constructor
  成员方法 Method

 反射:
class字节码文件对象 -- 去得到对应的成员对象 -- 通过该成员的对象调用方法使用
 
 通过反射获取构造方法并使用
(3)案例:
 1.通过反射获取构造方法
 public Constructor[] getConstructors() 获取公共的构造方法
 public Constructor[] getDeclaredConstructors() 获取所有的构造方法(包括私有)
 public Constructor getConstructor(Class... parameterTypes) 根据构造参数获取公共的指定构造
 public Constructor getDeclaredConstructor(Class<?>... parameterTypes) 根据构造参数获取指定构造(包括私有,但是私有在使用的时候需要取消访问限制)
2.通过反射获取构造方法并创建对象
  public T newInstance(Object... initargs)

创建一个Preson类,里面有带不同修饰符的成员变量,成员方法和构造方法;

package com.edu_01;public class Person {//创建三个成员变量String name;public int age;private String address;//创建几个构造方法public Person(){}Person(String name){this.name = name;}protected Person(String name,int age){this.name = name;this.age = age;}private Person(String name,int age,String address){this.name = name;this.age = age;this.address = address;}//创建一个成员方法public void method(){System.out.println("method");}void function(int price){System.out.println(price);}private void show(String husband,String wife){System.out.println(husband+"--"+wife);}@Overridepublic String toString() {return "Person [name=" + name + ", age=" + age + ", address=" + address+ "]";}}

public class ConstructorDemo {public static void main(String[] args) throws Exception {//public Constructor[] getConstructors() 获取公共的构造方法//获取Peroson类对应的字节码文件对象Class c = Class.forName("com.edu_01.Person");//获取Perosn类中对应的构造方法Constructor[] cons = c.getConstructors();//遍历所有获取到的构造方法for (Constructor con : cons) {System.out.println(con);//public com.edu_01.Person()}System.out.println("----------------");//public Constructor[] getDeclaredConstructors() 获取所有的构造方法(包括私有)Constructor[] cons2 = c.getDeclaredConstructors();for (Constructor con : cons2) {System.out.println(con);}System.out.println("---------------");//public Constructor getConstructor(Class... parameterTypes) 根据构造参数获取公共的指定构造//获取Person类中的公共的无参数的构造方法Constructor con  = c.getConstructor();System.out.println(con);//怎么通过我们刚才获取的无参构造创建对象//public T newInstance(Object... initargs)Object obj = con.newInstance();System.out.println(obj);System.out.println("-------------");//获取Person类中的非公共的构造方法//public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)Constructor con2 = c.getDeclaredConstructor(String.class,int.class,String.class);//获取构造器的时候,传入的什么参数,在调用获取到的这个构造方法对象的时候也就需要传入什么类型的参数//取消这个构造器对象所对应的访问权限检测con2.setAccessible(true);Object obj2 = con2.newInstance("陈奕迅",45,"香港");System.out.println(obj2);System.out.println("-----------");//获取被Protected修饰的构造方法Constructor con3 = c.getDeclaredConstructor(String.class,int.class);//取消访问权限的检测con3.setAccessible(true);Object obj3 = con3.newInstance("张学友",50);System.out.println(obj3);}}

protect和private修饰的构造方法需要取消访问权限检测 setAccessible(true)

 3.通过反射获取成员变量并使用
 public Field[] getFields()获取公有的成员变量
 public Field[] getDeclaredFields()获取全部的成员变量,包括私有
 public Field getDeclaredField(String name) 传入变量名称返回指定的成员变量对象,包括私有
 public Field getField(String name)传入变量名称返回指定的成员变量对象,仅可获取共有的
 public void set(Object obj,Object value)给一个对象的一个字段设置一个值

public class FieldDemo {public static void main(String[] args) throws Exception {//获取Perosn类对应的字节码文件对象Class c = Class.forName("com.edu_01.Person");//利用反射获取一个[Perosn对象Constructor con = c.getConstructor();Object obj = con.newInstance();//获取所有公共的字段对象//public Field[] getFields()获取公有的成员变量Field[] fields = c.getFields();for (Field field : fields) {System.out.println(field);}System.out.println("--------------");//获取所有的字段对象,包括私有//public Field[] getDeclaredFields()获取全部的成员变量,包括私有Field[] fields2 = c.getDeclaredFields();for (Field field : fields2) {System.out.println(field);}System.out.println("--------------");//public Field getDeclaredField(String name) 传入变量名称返回指定的成员变量对象,包括私有//获取String name;字段Field f = c.getDeclaredField("name");//使用f这个对象给一个Perosn对象设置姓名//public void set(Object obj, Object value)/** * 参数1:需要设置的对象 * 参数2:需要给这个对象设置什么值 *///取消访问修饰符的限制f.setAccessible(true);f.set(obj, "谢娜");System.out.println("--------------");//public Field getField(String name)传入变量名称返回指定的成员变量对象,仅可获取公有的//获取public int age;Field f2 = c.getField("age");f2.set(obj, 30);System.out.println("---------------");Field f3 = c.getDeclaredField("address");//取消权限检测f3.setAccessible(true);f3.set(obj, "湖南");System.out.println(obj);}}

 4.通过反射获取成员方法并使用
 public Method[] getMethods()获取所有公共成员方法,包括父类的
 public Method[] getDeclaredMethods()获取所有本类成员方法,包括私有
 public Method getMethod(String name, Class<?>... parameterTypes)参数一:方法名 参数二:方法参数类型.class 获取指定的公共方法
 public Method getDeclaredMethod(String name,Class<?>... parameterTypes)参数一:方法名 参数二:方法参数类型.class 获取指定的方法,包括私有
 Object invoke(Object obj, Object... args) 让某一个对象使用这个方法,并且传入参数
 
public class MethodDemo {public static void main(String[] args) throws Exception {//1.获取Person类中所有的公有的成员方法//创建Pwrson类对应的字节码文件对象Class c = Class.forName("com.edu_01.Person");//利用反射的方式创建一个Person对象Constructor con  = c.getConstructor();Object obj = con.newInstance();//public Method[] getMethods()获取所有公共成员方法,包括父类的公共的成员方法Method[] methods = c.getMethods();for (Method method : methods) {System.out.println(method);}System.out.println("---------------");//获取Person类中的所有的成员方法//public Method[] getDeclaredMethods()获取所有成员方法,包括私有,只能获取本类的所有的成员方法,不能获取父类的Method[] methods2 = c.getDeclaredMethods();for (Method method : methods2) {System.out.println(method);}System.out.println("---------------");// public Method getMethod(String name, Class<?>... parameterTypes)//参数一:方法名 参数二:方法参数类型.class 获取指定的公共方法//获取method()这个公有的成员方法Method m  = c.getMethod("method");System.out.println(m);System.out.println("---------------");//public Method getDeclaredMethod(String name,Class<?>... parameterTypes)//参数一:方法名 参数二:方法参数类型.class 获取指定的方法,包括私有//需求获取method这个个方法Method m2 = c.getDeclaredMethod("method");System.out.println(m2);//使用m2这个成员方法的对象//public Object invoke(Object obj,Object... args)/** * 参数1:执行m2这个方法的对象 * 参数2:执行m2这个方法的时候,需要传入的参数 */m2.invoke(obj);System.out.println("-----------------");//获取Person类中function方法Method m3 = c.getDeclaredMethod("function", int.class);//取消权限检测m3.setAccessible(true);m3.invoke(obj, 10);System.out.println("-------------------");Method m4 = c.getDeclaredMethod("show", String.class,String.class);m4.setAccessible(true);m4.invoke(obj, "张杰","谢娜");}}

(4)面试题:
1:通过反射运行配置文件的内容

public class Test {public static void main(String[] args) throws Exception {//创建Properties集合Properties prop = new Properties();prop.load(new FileReader("prop.txt"));//从集合中获取对应的全类名和对应的需要执行的方法名String className = prop.getProperty("className");String classMethod = prop.getProperty("classMethod");//创键配置文件中类对应的对象Class c = Class.forName(className);Constructor con = c.getConstructor();Object obj = con.newInstance();//通过反射获取需要执行的方法对象Method m = c.getMethod(classMethod);//执行获取到的方法m.invoke(obj);}}
这里只需要修改prop.txt文件中,className和classMethod对应的类对象名称和类方法,就可以调用该类中的方法。


2:我给你ArrayList<Integer>的一个对象,我想在这个集合中添加一个字符串数据,如何实现呢?

案例分析:

 1.创建ArrayList类对应的字节码文件对象
  2.通过字节码文件对象获取到add方法对象
  3.调用add方法的对象,给集合中添加字符串 

public class ArrTest {public static void main(String[] args) throws Exception {//1.创建ArrayList类对应的字节码文件对象ArrayList<Integer> arr = new ArrayList<>();Class c = arr.getClass();//2.通过字节码文件对象获取到add方法对象Method addM = c.getDeclaredMethod("add", Object.class);//3.调用add方法的对象,给集合中添加字符串addM.invoke(arr, "java");addM.invoke(arr, "world");System.out.println(arr);}}
 

泛型:泛型其实是给jvm虚拟机看的,当程序一旦运行起来之后,泛型会自动去掉,这也叫泛型擦出






阅读全文
0 0