类的加载
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。
加载
就是指将class文件读入内存,并为之创建一个Class对象。
任何类被使用时系统都会建立一个Class对象。
连接
验证 是否有正确的内部结构,并和其他类协调一致
准备 负责为类的静态成员分配内存,并设置默认初始化值
解析 将类的二进制数据中的符号引用替换为直接引用
初始化 就是我们以前讲过的初始化步骤
类加载器
负责将.class文件加载到内在中,并为之生成对应的Class对象。
虽然我们不需要关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行。
类加载器的组成
Bootstrap ClassLoader 根类加载器
Extension ClassLoader 扩展类加载器
Sysetm ClassLoader 系统类加载器
类加载器的作用:
Bootstrap ClassLoader 根类加载器
也被称为引导类加载器,负责Java核心类的加载
比如System,String等。在JDK中JRE的lib目录下rt.jar文件中
Extension ClassLoader 扩展类加载器
负责JRE的扩展目录中jar包的加载。
在JDK中JRE的lib目录下ext目录
Sysetm ClassLoader 系统类加载器
负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径
反射
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.
获取Class文件对象:
/*
*
* 反射就是使用class文件对象,去使用该文件中的成员变量,构造方法,成员方法
* Person p = new Person();
* p...
*
*
* 使用反射,需要得到class文件对象,其实也就是得到Class类的对象
* Class类
* 成员变量 Field
* 构造方法:Constructor
* 成员方法:Method
*
*获取Class文件对象的方式
*1.Object类的getclass()方法
*2.数据类型的静态属性
*3.Class.forName()
*
*使用选择:
*开发环境下使用第三种方式,因为第三种是一个字符串,而不是一个具体的类名,可以配置到配置文件中
*
* */
public class Reflectdemo {
public static void main(String[] args) throws ClassNotFoundException {
//-----方式一--------
Person p = new Person();
Class c = p.getClass();
Person p2 = new Person();
Class c2 = p.getClass();
System.out.println(p == p2); //false
System.out.println(c == c2); //true Class文件对象都是同一个
//------方式二-------------
Class c3 = Person.class;
System.out.println(c == c3); //true
//-----方式三---------------
//带包名的路径demo.Person
Class c4 = Class.forName("demo.Person");
System.out.println(c == c4); //true
}
}
通过反射获取构造方法:
package demo;
public class Person {
String name;
int age;
String adress;
public Person() {
}
Person(String name,int age) {
this.name = name;
this.age = age;
}
public Person(String name,int age,String adress) {
this.name = name;
this.age = age;
this.adress = adress;
}
public void show() {
System.out.println("show");
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", adress=" + adress + "]";
}
}
package demo_1;
import java.lang.reflect.Constructor;
/*通过反射获取构造方法并使用
*
* */
public class Reflect_demo {
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
//获取字节码文件对象:
Class c = Class.forName("demo.Person");
//获取构造方法
//public Constructor[] getConstructors() 所表示的类的所有公共构造方法
//public Constructor<?>[] getDeclaredConstructors() 所有构造方法
/*Constructor[] cons = c.getDeclaredConstructors();
for(Constructor con : cons) {
System.out.println(con);
}*/
//获取单个构造方法:
//public Constructor<T> getConstructor(Class<?>... parameterTypes)
//参数表示的是:你要获取的构造方法的构造参数个数和数据类型的Class字节码文件对象
Constructor con = c.getConstructor();
//使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例
Object obj = con.newInstance();
System.out.println(obj);
//person p = new person() system.out.println(p);上面那么多酒相当于这两句
}
}
通过反射获取构造方法,并且使用
package demo_1;
import java.lang.reflect.Constructor;
/*通过反射获取构造方法,并且使用
Person(String name,int age,String adress)
Person p = new Person("xiaoming",25,"nanjing");
System.out.println(p)*/
public class Reflect_demo2 {
public static void main(String[] args) throws Exception{
//获取字节码文件对象
Class c = Class.forName("demo.Person");
//获取带参数构造方法:
//public Constructor<T> getConstructor(Class<?>... parameterTypes)
Constructor con = c.getConstructor(String.class,int.class,String.class);
//通过带参构造方法创建对象
Object obj = con.newInstance("xiaoming",25,"nanjing");
System.out.println(obj);
}
}
//控制台返回
//Person [name=xiaoming, age=25, adress=nanjing]
通过反射获取私有构造方法并且使用
package demo_1;
import java.lang.reflect.Constructor;
/*通过反射获取私有构造方法并且使用;
* Person p = new person("zhangsan");
*
*
*
* */
public class Reflect_demo3 {
public static void main(String[] args) throws Exception {
//获取字节码文件对象
Class c = Class.forName("demo.Person");
//获取构造方法
//java.lang.NoSuchMethodException Constructor<T> getConstructor(Class<?>... parameterTypes)
//getDeclaredConstructor(Class<?>... parameterTypes) 可以获取私有构造方法
Constructor con = c.getDeclaredConstructor(String.class);
//java.lang.IllegalAccessException
con.setAccessible(true);
//创建对象
Object obj = con.newInstance("zhangsan");
System.out.println(obj);
}
}
获取成员变量并且赋值:
package demo_1;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
/*获取成员变量并且赋值
*
*
*
*
* */
public class Reflect_demo4 {
public static void main(String[] args) throws Exception{
//获取字节码文件对象
Class c = Class.forName("demo.Person");
//获取成员变量
//---获取所有成员变量
/*Field[] fds = c.getDeclaredFields();
for(Field fd:fds) {
System.out.println(fd);
}
*/
//获取构造方法
Constructor con = c.getConstructor();
Object obj = con.newInstance();
System.out.println(obj);
//获取单个成员变量
Field fd = c.getField("adress");
//获取字段的值
Object value = fd.get(obj);
//获取字段类型
Class type = fd.getType();
//将指定对象变量上此 Field 对象表示的字段设置为指定的新值
//public void set(Object obj,Object value)
fd.set(obj, "shanghai");
System.out.println(obj);
//获取name并且赋值
Field fd2 = c.getDeclaredField("name");
/*Constructor con = c.getConstructor();
Object obj = con.newInstance();
System.out.println(obj);*/
fd2.setAccessible(true);
fd2.set(obj, "zhangsan");
System.out.println(obj);
}
}
//返回
//Person [name=null, age=0, adress=null]
//Person [name=null, age=0, adress=shanghai]
//Person [name=zhangsan, age=0, adress=shanghai]
获取静态方法使用:
public void test(){
class clazz = Class.forName("xx");
Method method = clazz.getMethod("aa1",int.class);
//静态方法不需要对象即可访问。
method.invoke(null,23)
}
获取main方法并且使用:
@Test
public void test(){
Class clazz = Class.forName("xxx");
Method method = clazz.getMethod("main",string[].class);
method.invoke(null,(Object)new String[]{"aa","bb"});
}
获取一般方法并且使用:
package demo_1;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import demo.Person;
//获取所有方法
public class Reflect_demo5 {
public static void main(String[] args) throws Exception {
//获取字节码文件对象
Class c = Class.forName("demo.Person");
//获取所有方法:
// Method[] getMethods()反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法
//Method[] getDeclaredMethods()
//反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。
/*Method[] mts = c.getDeclaredMethods();
for (Method mt:mts) {
System.out.println(mt);
}*/
//Person p = new person();p.show();
//创建对象
Constructor con = c.getConstructor();
Object obj = con.newInstance();
//public Method getMethod(String name,Class<?>... parameterTypes)
//第一个参数表示字符串形式的方法名,第二个表示方法参数的class类型
Method mt1 = c.getMethod("show");
//public Object invoke(Object obj,Object... args)
//第一个参数表示对象,第二个表示调用该方法的实际参数。
mt1.invoke(obj);//调用obj对象的mt1方法。
System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
//public void toshow(String name)
Method mt2 = c.getMethod("toshow", String.class);
mt2.invoke(obj, "123");
//public String toStr()
Method mt3 = c.getMethod("toString", String.class,int.class,String.class);
Object stringobj = mt3.invoke(obj, "zhangsan",15,"wuhan");
System.out.println(stringobj);
}
}
反射的使用小案例 :
public class Student {
public void love() {
System.out.println("爱生活,爱学习!");
}
}
public class Teacher {
public void love() {
System.out.println("爱生活,爱睡觉!");
}
}
public class Worker {
public void love() {
System.out.println("爱生活,爱工作!");
}
}
//创建的配置文件class.txt
//只需要修改配置文件即可方便的调用不同类的方法。
className=reflect.Student
methodName=love
调用:
package reflect;
import java.io.FileReader;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Properties;
/*使用反射提高灵活度。
* 可动态执行
* 需要使用配置文件配合使用
* 用class。txt代替
* 设置两个键值
* className
* methodName
*
*
* */
public class Pro {
public static void main(String[] args) throws Exception{
//用反射前
/*Student s = new Student();
s.love();
Teacher t = new Teacher();
t.love();
Worker w = new Worker();
w.love();*/
//使用反射
//加载键值对数据
Properties pro = new Properties();
FileReader fr = new FileReader("class.txt");
pro.load(fr);
fr.close();
//获取数据
String className = pro.getProperty("className");
String methodName = pro.getProperty("methodName");
//反射
Class c = Class.forName(className);
//无参创造对象
Constructor cons = c.getConstructor();
Object obj = cons.newInstance();
//调用方法
Method md = c.getMethod(methodName);
md.invoke(obj);
}
}
通过反射向Arreylist<integer> 添加字符串
package reflect;
import java.lang.reflect.Method;
import java.util.ArrayList;
/*Arreylist<integer>
添加字符串
*/
public class List {
public static void main(String[] args) throws Exception{
//创建集合对象
ArrayList<Integer> al = new ArrayList<Integer>();
al.add(1);
al.add(1);
al.add(3);
al.add(2);
System.out.println(al);
//反射
Class c = al.getClass();
//获取方法 标识该方法形参类型的 Class 对象的一个数组
Method md = c.getMethod("add",Object.class);
md.invoke(al, "hello");
System.out.println(al);
}
}
通过反射写一个工具能够将某对象的某个属性设置为某个值;
tools:
package reflect;
import java.lang.reflect.Field;
public class Tool {
public void SetProperty(Object obj,String propertyname,Object value) throws Exception{
//得到字节码文本对象
Class c = obj.getClass();
//获取该对象的成员变量propertyname
Field fd = c.getDeclaredField(propertyname);
//暴力访问取消访问检查
fd.setAccessible(true);
//给对象的成员变量赋值
fd.set(obj, value);
}
}
运用:
package reflect;
public class Tooldemo {
public static void main(String[] args) throws Exception {
person p = new person();
Tool t = new Tool();
t.SetProperty(p,"name","zhangsan" );
t.SetProperty(p,"age", 28);
System.out.println(p.toString());
worker w = new worker();
t.SetProperty(w, "sex", '女');
t.SetProperty(w, "price", 12.12f);
System.out.println(w);
}
}
class person {
private String name;
int age;
@Override
public String toString() {
return name + "-------" + age;
}
}
class worker{
char sex;
float price;
@Override
public String toString() {
// TODO Auto-generated method stub
return sex + "------" + price;
}
}
动态代理:
通过反射来生成一个代理(作用是在一个功能前后批量添加功能)
在Java中java.lang.reflect包下提供了一个Proxy类和一个InvocationHandler接口,通过使用这个类和接口就可以生成动态代理对象。JDK提供的代理只能针对接口做代理。我们有更强大的代理cglib
Proxy类中的方法创建动态代理类对象
•public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
•最终会调用InvocationHandler的方法
InvocationHandler
Object invoke(Object proxy,Method method,Object[] args)
Proxy类中创建动态代理对象的方法的三个参数;
ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的invoke 方法来进行调用。
InvocationHandler接口中invoke方法的三个参数:
proxy:代表动态代理对象
method:代表正在执行的方法
args:代表调用目标方法时传入的实参
Proxy.newProxyInstance
创建的代理对象是在jvm运行时动态生成的一个对象,它并不是我们的InvocationHandler类型,
也不是我们定义的那组接口的类型,而是在运行是动态生成的一个对象,并且命名方式都是这样的形式,
以$开头,proxy为中,最后一个数字表示对象的标号。
System.out.println(u.getClass().getName());
例子:
接口:
public interface StudentDao {
public abstract void login();
public abstract void regester();
}
public class StudentDaoimpl implements StudentDao{
@Override
public void login() {
// TODO Auto-generated method stub
System.out.println("login");
}
@Override
public void regester() {
// TODO Auto-generated method stub
System.out.println("regester");
}
}
package proxy;
import java.lang.reflect.Proxy;
/*Proxy类中的方法创建动态代理类对象
•public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
•最终会调用InvocationHandler的方法
InvocationHandler
Object invoke(Object proxy,Method method,Object[] args)*/
public class test {
public static void main(String[] args) {
// TODO Auto-generated method stub
StudentDao st = new StudentDaoimpl();
//创建一个动态代理对象
//proxy中有个方法可以创建动态代理对象public static Object newProxyInstance(ClassLoader loader,
//Class<?>[] interfaces,
//InvocationHandler h)
//对st对象做一个代理对象
myInvocationHandler handler = new myInvocationHandler(st);
//Object pro = Proxy.newProxyInstance(st.getClass().getClassLoader(), st.getClass().getInterfaces(), handler);
StudentDao pro = (StudentDao)Proxy.newProxyInstance(st.getClass().getClassLoader(), st.getClass().getInterfaces(), handler);
pro.login();
}
}