Java类加载器和反射
1. 类的加载
当程序要使用某个类的时候,如果该类还没有被加载到内存中,则系统会通过加载连接,初始化三个步骤来实现对这个类的初始化
加载:将Class文件读入内存,并创建一个class对象,任何类被使用时系统都会建立一个Class对象
连接:验证:是否有正确的内部结构,并和其它类协调一致
准备:负责为类的静态成员分配内存,并设置为默认初始化值
解析:将类的二进制数据中的符号引用替换为直接引用
类初始化时机:
1. 方法类的静态变量,或者为静态变量赋值
2. 调用类的静态方法
3. 创建类的实例
4. 使用反射的方式来强制创建某个类或者接口对应的java.lang.Class对象
5. 初始化某个类的对象
6. 直接使用java.exe命令来运行某个主类
类加载器:
负责将class文件加载到内存中,并为之生成对应的Class对象
类加载器的组成:
Bootstrap ClassLoader:根类加载器
也被称为引导类加载器,负责java核心类的加载:比如System,String等,在JDK中JRE的lib目录下rt.jar文件
Extension ClassLoader:扩展类加载器
负责JRE的扩展目录中jar包的加载,在JDK中JRE的lib目录下ext目录
System ClassLoader:系统类加载器
负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径
2. 反射
java反射机制是在运行状态中,对于任意一个类,都能够直到这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性,这种动态获取的信息以及动态调用对象的方法的功能称为反射机制,简单来说就是通过Class文件,来使用该文件中的成员变量,构造方法,成员方法。
如果想要解剖一个类,必须先要获取该类的字节码文件对象,而解剖使用的就是class类中的方法,所以要先获取到每个字节码文件对应的Class类型的对象
使用步骤
A:获取Class文件对象(Class类对象)
1. 通过Object类的getClass()方法
2. 通过数据类型的静态属性class
3. Class s = Class.forName("类名"),类名需要是完整路径,包名.类名
B:Class类:
成员变量:Field
构造方法:Constructor
获取所有公共的构造方法:public Constructor[] getConstructors();
获取所有的构造方法:public Constructor[] getDeclaredConstructors();
获取指定的构造方法:public Constructor<T> getConstructor(Class<?>... parameterTypes)
成员方法:Method
public Method getMethod(String name,Class<?>... parameterTypes)
第一个参数表示的方法名,第二个参数表示的是方法的参数的class类型
public Object invoke(Object obj,Object... args)
返回值是Object接收,第一个参数表示对象是谁,第二参数表示调用该方法的实际参数
public class Reflect { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchFieldException { Person p = new Person(); Person e = new Person(); Class c = p.getClass(); Class l = e.getClass(); // Object的getClass()获取 Class<Person> a = Person.class; // class静态属性 Class s = Class.forName("day_07.Person"); System.out.println(p == e); // false System.out.println(c == l); // true System.out.println(c == a); // true System.out.println(c == s); // true Constructor[] cons = s.getConstructors(); // 返回所有的公共构造方法 // Constructor[] cons = s.getDeclaredConstructors(); // 所有的构造方法 for(Constructor con : cons){ System.out.println(con); } /* public day_07.Person() * public day_07.Person(java.lang.String,int) */ //使用无参构造 Constructor con = c.getConstructor(); Object obj = con.newInstance(); System.out.println(obj); /* public T newInstance(Object... initargs) * 使用该Constructor对象表示的构造方法来创建该构造方法的申明类的新实例,并用指定的参数初始化该实例 */ //使用带参构造 con = c.getConstructor(String.class, int.class); Object obe = con.newInstance("xiaojignzi", 20); System.out.println(obe); // 获取私有构造方法 con = c.getDeclaredConstructor(String.class); con.setAccessible(true); // 设置私有可以访问 obe = con.newInstance("小镜子"); System.out.println(obe); // 获取成员变量并使用 Field[] fields = c.getDeclaredFields(); for(Field field : fields){ System.out.println(field); } // 获取单个(私有)成员变量,获取name并对其赋值 con = c.getConstructor(); obj = con.newInstance(); Field nameField = c.getDeclaredField("name"); // name是私有的修饰,所以需要加declared nameField.setAccessible(true); nameField.set(obj, "北京"); // 给obj对象的AddressField的字段设置为北京 System.out.println(obj); // 获取所有方法 // Method[] methods = c.getMethods(); // 获取子类和父类的所有方法 Method[] methods = c.getDeclaredMethods(); // 获取子类的所有方法 for(Method method : methods){ System.out.println(method); } // 获取单个方法并使用 Method mt = c.getMethod("toString"); String st = (String) mt.invoke(obj); System.out.println(st); mt = c.getMethod("setName", String.class); mt.invoke(obj, "wangwang"); System.out.println(obj); // 获取私有单个方法 mt = c.getDeclaredMethod("getMessage"); mt.setAccessible(true); mt.invoke(obj); } }
通过反射运行配置文件:
class.txt: className = day_07.Person methodName = getName public class Runfile { public static void main(String[] args) throws Exception{ //加载键值对数据 Properties prop = new Properties(); FileReader fr = new FileReader("class.txt"); prop.load(fr); fr.close(); // 获取数据 String className = prop.getProperty("className"); String methodName = prop.getProperty("methodName"); Class c = Class.forName(className); Constructor con = c.getConstructor(); Object obj =con.newInstance(); Method m = c.getMethod(methodName); System.out.println(m.invoke(obj)); } }
通过反射写一个通用的设置某个对象的某个属性为指定值
public class Tool { public void setProperty(Object obj, String propertyName, Object value) throws Exception, SecurityException{ // 根据对象获取字节码文件对象 Class c = obj.getClass(); // 获取该对象的propertyName成员变量 Field field = c.getDeclaredField(propertyName); // 取消访问检查 field.setAccessible(true); // 给对象的成员变量赋值为指定值 field.set(obj, value); } } public class UseTool { public static void main(String[] args) throws Exception{ Person p = new Person(); Tool t = new Tool(); t.setProperty(p, "name", "xiaojignzi"); } }
3. 动态代理:
代理:本来自己应该做的事情,请了别人来做,被请的人就是代理对象
动态代理:在程序运行过程中产生的对象
程序的运行过程中产生对象其实就是反射的内容,动态代理就是通过反射来生成一个代理
在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[]):处理代理实例上的方法调用并返回结果
获取系统的时间
public class GetTime { public static void main(String[] args) { long start = System.currentTimeMillis(); for(int x = 0; x < 1000; x++){ System.out.println(x); } long end = System.currentTimeMillis(); System.out.println(end - start); } }
4. 模板设计模式
模板方法模式就是定义一个算法的骨架,把具体的实现算法延迟到子类中实现
优点:使用模板方法模式,在定义算法骨架时,可以灵活的实现具体的算法,满足用户灵活多变的需要
缺点:如果算法骨架有修改的话,需要修改抽象类
public class Timetest { public static void main(String[] args){ GetTime gt = new UseGetTime(); System.out.println(gt.getTime() + "毫秒"); } } public class UseGetTime extends GetTime { @Override public void code() { // TODO Auto-generated method stub BufferedOutputStream bos = null; BufferedInputStream bis = null; try{ bos = new BufferedOutputStream(new FileOutputStream("copy.jpg")); bis = new BufferedInputStream(new FileInputStream("abc.jpg")); byte[] bys = new byte[1024]; int len = 0; while((len = bis.read(bys)) != -1){ bos.write(bys, 0, len); } bis.close(); bos.close(); }catch(IOException e){ e.printStackTrace(); } } } public abstract class GetTime { public long getTime(){ long start = System.currentTimeMillis(); code(); long end = System.currentTimeMillis(); return end - start; } public abstract void code(); }
5. 装饰模式
装饰模式就是使用被装饰类的一个子类的实例,在客户端将这个子类的实例交给装饰类,是继承的替代方案
优点:通过装饰模式,可以提供比继承更加灵活的扩展对象的功能,它可以动态的添加对象的功能,并且可以随意的组合这些功能
缺点:正是因为可以随意组合,所以可能出现一些不合理的逻辑
装饰模式可以任意组合
public interface Phone { public abstract void call(); } public class PhoneDecorate implements Phone{ private Phone p; public PhoneDecorate(Phone p) { super(); this.p = p; } @Override public void call() { this.p.call(); } } public class IPhone implements Phone { @Override public void call() { // TODO Auto-generated method stub System.out.println("手机可以打电话"); } } public class MusicPhoneDecorate extends PhoneDecorate{ public MusicPhoneDecorate(Phone p) { super(p); // TODO Auto-generated constructor stub } @Override public void call(){ super.call(); System.out.println("手机可以听音乐"); } } public class RingPhoneDecorate extends PhoneDecorate { public RingPhoneDecorate(Phone p) { super(p); // TODO Auto-generated constructor stub } @Override public void call(){ System.out.println("手机可以听彩铃"); super.call(); } } public class PhoneUse { public static void main(String[] args) { Phone p = new IPhone(); PhoneDecorate pd = new RingPhoneDecorate(p); pd.call(); pd = new MusicPhoneDecorate(p); pd.call(); System.out.println("------------"); // 装饰模式可以任意组合 pd = new RingPhoneDecorate(new MusicPhoneDecorate(p)); pd.call(); // 以下也是装饰 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); Scanner sc = new Scanner(System.in); } }
6. 枚举
将变量的值一一列出来,变量的值只限于列举出来的值范围内。
注意事项:
定义枚举类要使用关键字enum
所有的枚举类都是Enum的子类
枚举类的第一行上必须是枚举项,最后一个枚举项的后面的分号是可以省略的,但是如果枚举类后面有其它东西,建议不要省略
枚举类可以有构造器,但是必须是private的,它默认也是private的,枚举类的用法比较特殊
枚举类可以在switch中使用
自己编写枚举类:
public abstract class Direction { String name; // 创建四个实例 public static final Direction FRONT = new Direction("前"){ @Override public void show() { // TODO Auto-generated method stub System.out.println("前"); } }; public static final Direction BEHIND = new Direction("后"){ @Override public void show() { // TODO Auto-generated method stub System.out.println("后"); } }; public static final Direction LEFT = new Direction("左"){ @Override public void show() { // TODO Auto-generated method stub System.out.println("左"); } }; public static final Direction RIGHT = new Direction("右"){ @Override public void show() { // TODO Auto-generated method stub System.out.println("右"); } }; // 构造私有,外界不能创建对象 private Direction(String name){ this.name = name; } public void getName(){ System.out.println(name); } public abstract void show(); } public class DirectionUse { public static void main(String[] args){ Direction d = Direction.FRONT; System.out.println(d); d.show(); } }
最简单的枚举实例:
public enum Directions { FRONT, BEHIND, LEFT, RIGHT; } public class DirectionsUse { public static void main(String[] args){ Directions d = Directions.FRONT; System.out.println(d); } }
特殊用法
public enum Directions { FRONT("前"), BEHIND("后"), LEFT("左"), RIGHT("右"); private String name; private Directions(String name){ this.name = name; } public String getName(){ return name; } } public class DirectionsUse { public static void main(String[] args){ Directions d = Directions.FRONT; System.out.println(d); System.out.println(d.getName()); } }
带抽象方法的枚举类
public enum Directions { FRONT("前"){ @Override public void show() { // TODO Auto-generated method stub System.out.println("前"); } }, BEHIND("后"){ @Override public void show() { // TODO Auto-generated method stub System.out.println("后"); } }, LEFT("左"){ @Override public void show() { // TODO Auto-generated method stub System.out.println("左"); } }, RIGHT("右"){ @Override public void show() { // TODO Auto-generated method stub System.out.println("右"); } }; private String name; private Directions(String name){ this.name = name; } public String getName(){ return name; } public abstract void show(); } public class DirectionsUse { public static void main(String[] args){ Directions d = Directions.FRONT; System.out.println(d); System.out.println(d.getName()); d.show(); } }
可以使用switch
switch(d){ case FRONT: System.out.println("你选择了前"); break; case BEHIND: System.out.println("你选择了后"); break; case LEFT: System.out.println("你选择了左"); break; case RIGHT: System.out.println("你选择了右"); break; }
枚举类的方法
public class EnumMethod { public static void main(String[] args){ Directions d1 = Directions.FRONT; Directions d2 = Directions.BEHIND; Directions d3 = Directions.LEFT; Directions d4 = Directions.RIGHT; System.out.println(d1.compareTo(d2)); // -1 System.out.println(d1.compareTo(d3)); // -2 System.out.println(d1.compareTo(d4)); // -3 // String name(); System.out.println(d1.name()); System.out.println(d2.name()); // int ordinal() System.out.println(d1.ordinal()); // 0 System.out.println(d2.ordinal()); // 1 System.out.println(d3.ordinal()); // 2 System.out.println(d4.ordinal()); // 3 // String toString() System.out.println(d1.toString()); // FRONT 可以重写 // <T> T valueOf(Class<T> type, String name) 创建一个name枚举类型的对象 Directions d5 = Enum.valueOf(Directions.class, "FRONT"); System.out.println(d5.getName()); // values() 此方法在文档中查询不到,但是每一个枚举类都具有该方法,它遍历枚举类的所有枚举值非常方便 Directions[] dirs = Directions.values(); for(Directions dir : dirs){ System.out.println(dir); // FRONT BEHIND LEFT RIGHT } } }
7. JDK7新特性
格式:try-with-resources语句
try(必须是java.lang.AutoCloseable的子类对象){}
好处:资源自动释放,不需要close()了
把该需要关闭的资源定义在这里
主要流体系的对象是这个接口的子类
public class TryNew { public static void main(String[] args){ try(FileReader fr = new FileReader("class.txt"); FileWriter fw = new FileWriter("cop.txt")){ int ch = 0; while((ch = fr.read()) != -1){ fw.write(ch); } }catch(IOException e){ e.printStackTrace(); } } }