类的声明周期:
1.加载:
把 .class 加载 到 方法区的内存中,
并且 创建 一个 对应的对象 在 堆中(Class);
2.连接
1)验证:
对字节码文件 .class进行验证。
2)准备
对静态变量 分配空间 ,进行默认初始化;
3)把 符号引用 替换 为 直接引用;。
3.初始化
对静态 变量 声明处 或者 静态块处初始化。
反射: 获得 运行时 类的信息。
反射就是把java类中的各种成分映射成一个个的Java对象
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制;
访问属性:
Field
getDeclaredFields(); 获得 所有的属性(包含私有)
常用方法: getName() 名
getType() 类型
getModifiers() 访问权限
访问方法:
Method
getDeclaredMethods() 获得所有方法
常用方法:getReturnType() 获得范围值类型
getParameterTypes() 获得参数列表
访问构造:
Constructor
getDeclaredConstructors(); //获得所有
常用方法:newInstance() 调用构造
测试加载器测试:
/**
直接调用加载器
static中的数据在编译器的时候就加载了
*/
public class TestDemo1 {
static{
System.out.println("static");
}
public static void main(String[] args) throws ClassNotFoundException {
Class.forName("com.qcj.fanshe.TestDemo1");//这句加不加,静态代码块都加载
}
}
三种加载器的区别:
/**
* 三种加载器的区别:
* 自定义加载器 -》 系统(应用)类加载器 -》 扩展类加载器 -》 根类加载器
*/
public class TestDemo2 {
public static void main(String[] args) {
//系统类(应用类)加载器 使用范围我们平时写的java在classpath路径下的经过编译的.class文件
ClassLoader loader = TestDemo2.class.getClassLoader();
System.out.println(loader);//看看谁加载的
//扩展类加载器 父加载器 父扩展 加载jdk/jre/lib/ext下的扩展jar包
System.out.println(loader.getParent());
//根加载器 加载jdk/jre/lib下的jar包
System.out.println(loader.getParent().getParent());
}
}
class.forName()和classLoader区别:
/**
class.forName()和classLoader区别:
java中class.forName()和classLoader都可用来对类进行加载。
class.forName()前者除了将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static块。
而classLoader只干一件事情,就是将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块。
*/
public class TestDemo3 {
public static void main(String[] args) throws ClassNotFoundException {
//加载类 完全限定命名
ClassLoader.getSystemClassLoader().loadClass("com.qcj.fanshe.Demo1");//只引起类的加载,不引起类的初始化
//加载类,并初始化 第二个参数:true(默认false) 引起初始化 第三个参数:系统类加载器
Class.forName("com.qcj.fanshe.Demo1",true,ClassLoader.getSystemClassLoader());
Class.forName("com.qcj.fanshe.Demo1");
}
}
class Demo1{
static{
System.out.println("Demo1 static");
}
}
自定义类加载器:
/** 自定义类加载器(重要)
* 注意:需要重写ClassLoader.loadClass中调用的findClass方法
* 主要用于自定义的class(也就是不在classpath范围内的class文件)
*
* 前期需要在d盘下,写一个C.java doc窗口编译 javac C.java 得到想要的 C.class文件
*/
public class TestDemo4 {
public static void main(String[] args) throws ClassNotFoundException {
//自定义类加载器 加载类 .class
MyLoader my = new MyLoader("d:/");
Class c = Class.forName("C",true,my);//是true 引起初始化(所以会调用static中的内容) false 不显示
System.out.println(c.getClassLoader());//获得是哪个类加载的
//断开
}
}
//自定义类加载器
class MyLoader extends ClassLoader{
String path;
MyLoader(String path){
this.path = path;
}
//需要重写findClass方法
//name 传进来的是完全限定命名
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
Class<?> c = null;
//"d:/" + "c" + ".class" -> "d:/c.class"
//有包名
//path = path + name.replace(".","/").concat(".class");//有包名就替
//没有包名
path = path + name.concat(".class");
//包名有了 需要用流读取
FileInputStream fin = null;
try {
fin = new FileInputStream(path);
byte[] b = new byte[fin.available()];
int len = fin.read(b);//读取class中内容到 b数组
c = this.defineClass(name,b,0,len);//把b中存在的东西解析成方法区能够识别的方式
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
fin.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return c;
}
}
安全管理器:
/**
* 安全管理器
*/
public class TestDemo5 {
public static void main(String[] args) throws IOException {
System.setSecurityManager(new MySecurityManage());
File f = new File("D:/C.class");
FileInputStream fileInputStream = new FileInputStream(f);
System.out.println(fileInputStream.read());
fileInputStream.close();
}
}
//自定义安全管理器 安全权限访问控制
class MySecurityManage extends SecurityManager{
@Override
public void checkRead(String file) {
if(file.endsWith(".class")){
throw new SecurityException("不能读取class文件");
}
}
}
反射:获得运行时类的信息:
/**
* 反射:获得运行时类的信息
* 属性
* 方法
* 构造方法
*/
public class TestDemo6 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
//从Person.class 中获取信息:反射
//步骤一:获得字节码文件对应的在堆中对象
//1. 方法1
//Class<Person> c2 = Person.class;
//2. 方法2 new
//Class<? extends Person> c1 = new Person().getClass();
//3. 方法3 完全限定命名
Class<?> c = Class.forName("com.qcj.fanshe.Person");
System.out.println("--------------属性-------------------");
//步骤二 : 有了对象后可以获得属性
//获得共有的属性
//Field[] fs = c.getFields();
//获取 所有的属性 共有跟私有
Field[] fs = c.getDeclaredFields();
for (Field f:fs) {
System.out.println(f.getName());//名
System.out.println(f.getType());//数据类型
System.out.println(Modifier.toString(f.getModifiers()));//访问权限 需要转换成string
}
Field f = c.getDeclaredField("no");
//如果不怎么也不允许破坏封装 用安全管理器
//System.setSecurityManager(new SecurityManager());
f.setAccessible(true);//是否有访问权限 破坏了封装
Object obj = c.newInstance();//获得一个实例对象 需要加入f.setAccessible(true);/
f.set(obj,22);//设置对象obj 的 no是22
System.out.println("f.get(obj):"+f.get(obj)); //得到对象obj的no属性的值
//-------------------------获得方法
System.out.println("------------方法-------------------");
Method[] ms = c.getDeclaredMethods();
for (Method m:ms) {
System.out.println(m.getName());
System.out.println(Modifier.toString(m.getModifiers()));
System.out.println(m.getReturnType());
System.out.println(Arrays.toString(m.getParameterTypes()));//返回参数列表
System.out.println("------");
}
//返回单个方法
Method m1 = c.getDeclaredMethod("f");
m1.invoke(obj);// 调用方法 (对象+参数)
//带返回值的
Method m2 = c.getDeclaredMethod("sf",String.class,int.class);//参数类型
String s = (String)m2.invoke(obj,"hel" , 2);
System.out.println("返回值:" + s);
System.out.println("----------------构造方法---------------------");
Constructor[] crs = c.getConstructors();
for (Constructor cr:crs) {
System.out.println(Arrays.toString(cr.getParameterTypes()));//参数类型
}
//单个构造
Constructor cr1 = c.getConstructor();//得到无参构造
cr1.newInstance();//用newInstance与用new是区别的,区别在于创建对象的方式不一样,前者是使用类加载机制
Constructor cr2 = c.getConstructor(int.class,String.class);//有参数构造
cr2.newInstance(111,"abc");
}
}
class Person{
private int no;
public String name;
public Person() {
System.out.println("无参数构造");
}
public Person(int no, String name) {
this.no = no;
this.name = name;
System.out.println("有参数构造");
}
public void f(){
System.out.println("无参数f");
}
public String sf(String str,int num){
System.out.println("带参数的sf");
return str + num;
}
}