26-java学习-类加载器、反射、动态代理、JDK1.5以及1.7的新特性

26-java学习-类加载器、反射、动态代理、JDK1.5以及1.7的新特性

目录:

类加载器
反射
动态代理
JDK1.5以及1.7的新特性

1.反射(类的加载概述和加载时机)

A:类的加载概述
	当程序要使用某个类时,如果该类还未被加载到内存中,
	则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。
	加载 
		就是指将class文件读入内存,并为之创建一个Class对象。
		任何类被使用时系统都会建立一个Class对象。
	连接
		验证 :	是否有正确的内部结构,并和其他类协调一致
		准备 :	负责为类的静态成员分配内存,并设置默认初始化值
		解析:	把类中的符号引用转换为直接引用
	初始化		就是我们以前讲过的初始化步骤

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

2.反射(类加载器的概述和分类)

A:类加载器的概述
	负责将.class文件加载到内在中,并为之生成对应的Class对象。
B:类加载器的分类
	Bootstrap ClassLoader 根类加载器
	Extension ClassLoader 扩展类加载器
	Sysetm ClassLoader 系统类加载器
C:类加载器的作用
	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包和类路径

3.反射(反射概述以及获取class文件对象的三种方式)

A:反射概述
	JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;
	对于任意一个对象,都能够调用它的任意一个方法和属性;
	这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
	要想解剖一个类,必须先要获取到该类的字节码文件对象。
	而解剖使用的就是Class类中的方法,所以先要获取到每一个字节码文件对应的Class类型的对象
B:获取class文件对象的三种方式
	a:Object类的getClass()方法
	b:静态属性class
	c:Class类中静态方法forName()
C:案例演示:	获取class文件对象的三种方式
		反射: 就是在运行状态中的一种动态调用方法或者属性的一种机制.
		- 就是获取字节码文件对象,然后剖析改类中存在哪些构造方法,哪些成员变量,哪些成员方法
- 
- 类的成员
- 成员变量		Field
- 构造方法		Constructor
- 成员方法		Method
- 
- 如何获取一个类对应的字节码文件对象:
- 
- a: 第一种通过Object类中的getClass方法
- b: 通过静态属性(class属性)
- c: 通过Class类中的一个静态方法:
- public static Class forName(String className): 
- className: 这个表示的是一个类对应的全类名(就是需要加上包名)
/**
 * 第一种通过Object类中的getClass方法
 */
Student s1 = new Student() ;
Student s2 = new Student() ;

Class clazz1 = s1.getClass() ;
Class clazz2 = s2.getClass() ;

System.out.println(clazz1 == clazz2);
System.out.println(clazz1);

System.out.println("-------------------------------------");

/**
 * 通过静态属性(class属性)
 */
Class clazz3 = Student.class ;
System.out.println(clazz1 == clazz3);

System.out.println("-------------------------------------");

/**
 * 通过Class类中的一个静态方法:
 * 	public static Class forName(String className): 
 * 	className: 这个表示的是一个类对应的全类名(就是需要加上包名)
 */
Class clazz4 = Class.forName("com.clik369.Student") ;
System.out.println(clazz2 == clazz4);

4.反射(通过反射获取无参构造方法并使用)

A:获取所有构造方法
	public Constructor<?>[] getConstructors() 获取所有的构造方法不包含私有的
	public Constructor<?>[] getDeclaredConstructors() 获取所有的构造方法 包括私有的
B:获取单个构造方法
	public Constructor<T> getConstructor(Class<?>... parameterTypes) 获取单个的构造方法 不包含私有的
	public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) 获取单个的构造方法包含私有的

5.反射(通过反射获取带参构造方法并使用)

A:案例演示:	通过反射获取带参构造方法并使用
// 获取字节码文件对象
	Class clazz  = Class.forName("com.click369.Student") ;
	
	// public Constructor<T> getConstructor(Class<?>... parameterTypes)
	// parameterTypes: 就是表示指定的构造方法上的参数的Class类型
	Constructor con = clazz.getConstructor(String.class) ;
	
	// 输出
	// System.out.println(con);
	
	// 使用con创建对象
	Object obj = con.newInstance("张三"); // 调用构造方法的时候传递的具体的参数
			
	// 输出
	System.out.println(obj);
	
	/**
	 * 之前创建对象的时候: Student s = new Student("张三") ;
	 */

6.反射(通过反射获取私有构造方法并使用)

A:案例演示:	通过反射获取私有构造方法并使用
	// 获取字节码文件对象
	Class clazz = Class.forName("com.click369.Student") ;
	Constructor con = clazz.getDeclaredConstructor(String.class , int.class) ;
	// 值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。
	con.setAccessible(true) ; 取消语法检查不然会报错 因为私有的外界不能直接访问
	Object obj = con.newInstance("张三" , 23) ;
	System.out.println(obj);

7.反射(通过反射获取成员变量并使用)

A:获取所有成员变量
	public Field[] getFields() 获取所有的成员变量包含从父类继承过来的
	public Field[] getDeclaredFields() 获取所有的成员变量 包含私有的 也包含从父类继承过来的成员变量
B:获取单个成员变量
	public Field getField(String name)
	public Field getDeclaredField(String name)
C:案例演示:	通过反射获取成员变量并使用
// 获取字节码文件对象
	Class clazz = Class.forName("com.click369.Student") ;
	Field[] fields = clazz.getFields() ;
	Field[] fields = clazz.getDeclaredFields(); 
	
	for(Field field : fields){
		System.out.println(field);
	}
	
	System.out.println("----------------------------------------------");
	Field field = clazz.getDeclaredField("name");
	/**
	 * public void set(Object obj,  Object value)
	 * obj: 表示的就是对象
	 * value: 表示的意思就是要具体赋的值
	 */
	Object obj = clazz.newInstance() ;
	field.set(obj, "张飞") ;// 表示的意思是: 给obj这个对象的name字段赋值为"张飞"
	
	/**
	 * 之前赋值: Student s = new Student() ;
	 * 			s.setName("张飞") ;			 
	 */
	
	// 获取age字段
	Field ageField = clazz.getDeclaredField("age") ;
	
	ageField.setAccessible(true) ;//取消语法检查  私有的外界不能直接访问
	ageField.set(obj, 23) ;
	
	// 输出obj
	System.out.println(obj);

8.反射(通过反射获取无参无返回值成员方法并使用)

A:获取所有成员方法
	public Method[] getMethods() //获取所有的公共的成员方法不包含私有的 包含从父类继承过来的过来的公共方法
	public Method[] getDeclaredMethods()//获取自己的所有成员方法 包含私有的
B:获取单个成员方法
	//参数1: 方法名称  参数2:方法行参的class 对象
	public Method getMethod(String name,Class<?>... parameterTypes) //获取单个的方法 不包含私有的
	public Method getDeclaredMethod(String name,Class<?>... parameterTypes) 获取单个方法包括私有的
C:案例演示:	通过反射获取无参无返回值成员方法并使用

	// 获取字节码文件对象
	Class clazz = Class.forName("com.click369.Student") ;
	Method[] methods = clazz.getDeclaredMethods() ;
	
	for(Method method : methods){
		System.out.println(method);
	}
	
	System.out.println("------------------------------------------");
	
	// public Method getMethod(String name, Class<?>... parameterTypes)
	// name: 表示的意思是方法的名称
	// parameterTypes: 表示的是指定的方法的参数对应的Class类型
	Method showMethod = clazz.getMethod("show") ;
	// System.out.println(showMethod);
	
	/**
	 * public Object invoke(Object obj, Object... args)
		obj: 	表示的是对象
		args: 	表示的是调用该方法的时候传递的具体的参数
		Object: 返回的是方法执行完毕以后的结果
	 */
	// 创建对象
	Object object = clazz.newInstance() ;
	showMethod.invoke(object) ;
	
	/**
	 * 
	 * 之前是: Student s = new Student()
	 * 		  s.show() ;
	 */

9.反射(通过反射获取带参带返回值成员方法并使用)

A:案例演示:	通过反射获取带参带返回值成员方法并使用
		// 获取字节码文件对象
	Class clazz = Class.forName("com.click369.Student") ;
	
	// 获取show方法
	Method method = clazz.getDeclaredMethod("show", String.class , int.class) ;
	
	// 创建一个对象
	Object obj = clazz.newInstance() ;
	
	// 调用方法
	Object result = method.invoke(obj, "张三" , 24) ;
	
	// 输出
	System.out.println(result);

10.反射(通过反射越过泛型检查)

//A:案例演示:	我给你ArrayList<Integer>的一个对象,我想在这个集合中添加一个字符串数据,如何实现呢?
// 创建一个ArrayList集合对象
	ArrayList<Integer> al = new ArrayList<Integer>() ;
	
	// 添加元素
	al.add(23) ;
	/**
	 * 现在这个al集合的泛型指定的是Integer,那么在编译器我们就没有办法往里面添加一个字符串.
	 * 因为泛型在编译存在语法检查,那么编译期完成不了,我们只能在运行期完成,因为泛型到了运行期
	 * 以后,泛型会自动擦除,那么要在运行期完成这个需求,那么我们就需要使用反射.
	 */
	Class clazz = al.getClass() ;
	Method method = clazz.getDeclaredMethod("add", Object.class) ;
	method.invoke(al, "nihoa") ;
	
	// 输出
	System.out.println("al: " + al);

11.反射(通过反射写一个通用的设置某个对象的某个属性为指定的值)

A:案例演示
	public void setProperty(Object obj, String propertyName, Object value){},
	此方法可将obj对象中名为propertyName的属性的值设置为value。

	public class PropertyTools {

private PropertyTools(){}

/**
 * 这个方法的作用: 就是给obj这个对象的propertyName这个属性设置上value这个值
 */
public static void setProperty(Object obj , String propertyName, Object value) throws Exception {
	
	// 获取字节码文件对象
	Class clazz = obj.getClass() ;
	
	// 获取propertyName对应的Field对象
	Field field = clazz.getDeclaredField(propertyName) ;
	
	// 取消语法检查
	field.setAccessible(true) ;
	
	// 调用set方法设置值
	field.set(obj, value) ;
	}
}

12.反射(动态代理的概述和实现)

A:动态代理概述
	代理:本来应该自己做的事情,却请了别人来做,被请的人就是代理对象。
	举例:春季回家买票让人代买
	动态代理:在程序运行过程中产生的这个对象
	而程序运行过程中产生对象其实就是我们刚才反射讲解的内容,所以,动态代理其实就是通过反射来生成一个代理
	
	在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)
B:案例演示:	动态代理的实现
我们可以通过Proxy类中的静态方法获取一个代理对象:
public static Object newProxyInstance(ClassLoader loader,  Class<?>[] interfaces,  InvocationHandler h)
loader: 			类加载器
interfaces:			接口对应的一个Class数组
InvocationHandler:	这个其实就是要代理对象所做的事情的一个类的封装

JDK给我们提供的动态代理,只能对接口进行代理.
loader: 			类加载器
interfaces:			接口对应的一个Class数组
InvocationHandler:	这个其实就是要代理对象所做的事情的一个类的封装

JDK给我们提供的动态代理,只能对接口进行代理.

13.JDK5新特性(JDK1.5的新特性回归以及自己实现枚举类)

A: JDK1.5的新特性: 自动拆装箱 , 泛型 , 增强for , 静态导入 , 可变参数 , 枚举
B:枚举概述:	就是一个类只能存在几个固定的对象,那么这个就是枚举.我们就可以使用这些对象可以表示一些固定的值.
			举例:一周只有7天,一年只有12个月等。
C:案例演示:	自己实现枚举类
/**枚举类
*/
public class Direction {

public static final Direction FRONT = new Direction() ;
public static final Direction BEHIND = new Direction() ;
public static final Direction LEFT = new Direction() ;
public static final Direction RIGHT = new Direction() ;

/**

- 私有化构造
  */
  private Direction() {}
  }
  public class Direction2 {
  public static final Direction2 FRONT = new Direction2("前") ;
public static final Direction2 BEHIND = new Direction2("后") ;
public static final Direction2 LEFT = new Direction2("左") ;
public static final Direction2 RIGHT = new Direction2("右") ;

// 定义成员变量
public String name ;

/**
 * 私有化构造
 */
private Direction2(String name) {
	this.name = name ;
}

14.JDK5新特性(通过enum实现枚举类)

/**- 定义枚举的格式:

- public enum 枚举名 {

- 枚举项1 , 枚举项2 , 枚举项3 .....

- }
   */
  public enum Direction {

  FRONT , BEHIND , LEFT  , RIGHT ;

## }
public enum Direction2 {
FRONT("前") , BEHIND("后") , LEFT("左")  , RIGHT("右") ;

public String name ;

private Direction2(String name){
	this.name = name ;
  }
}
public enum Direction3 {
FRONT("前"){
	@Override
	public void show() {
		System.out.println(name);
	}
}, BEHIND("后"){
	@Override
	public void show() {
		System.out.println(name);
	}
} , LEFT("左"){
	@Override
	public void show() {
		System.out.println(name);
	}
}  , RIGHT("右"){
	@Override
	public void show() {
		System.out.println(name);
	}
} ;

public String name ;

private Direction3(String name){
	this.name = name ;
}

// 抽象方法
public abstract void show() {
}
	测试

public static void main(String[] args) {
	// 测试
	Direction front = Direction.FRONT ;
	Direction behind = Direction.BEHIND;
	Direction left = Direction.LEFT ;
	Direction right = Direction.RIGHT ;
	
	// 输出
	System.out.println(front);
	System.out.println(behind);
	System.out.println(left);
	System.out.println(right);
	
	System.out.println("----------------------------------");
	
	//测试
	Direction2 front2 = Direction2.FRONT ;
	Direction2 behind2 = Direction2.BEHIND;
	Direction2 left2 = Direction2.LEFT ;
	Direction2 right2 = Direction2.RIGHT ;
	
	System.out.println(front2.name);
	System.out.println(behind2.name);
	System.out.println(left2.name);
	System.out.println(right2.name);
	
	System.out.println("----------------------------------");
	
	//测试
	Direction3 front3 = Direction3.FRONT ;
	Direction3 behind3 = Direction3.BEHIND;
	Direction3 left3 = Direction3.LEFT ;
	Direction3 right3 = Direction3.RIGHT ;
	
	System.out.println(front3.name);
	System.out.println(behind3.name);
	System.out.println(left3.name);
	System.out.println(right3.name);
	
	System.out.println("----------------------------");
	
	// 调用方法
	front3.show() ;
	behind3.show() ;
	left3.show() ;
	right3.show() ;
	
}

15.JDK5新特性(枚举的注意事项)

A:定义枚举类要用关键字enum
  所有枚举类都是Enum的子类
  枚举类的第一行上必须是枚举项,最后一个枚举项后的分号是可以省略的,但是如果枚举类有其他的东西,这个分号就不能省略。建议不要省略
	枚举类可以有构造器,但必须是private的,它默认的也是private的。枚举项的用法比较特殊:枚举(“”);
	枚举类也可以有抽象方法,但是枚举项必须重写该方法
	枚举在switch语句中的使用

16.JDK5新特性(枚举类的常见方法)

A:枚举类的常见方法
	int ordinal()  返回枚举项的序号
	int compareTo(E o) 比较两个枚举项的 返回的是两个枚举项序号的 差值
	String name() 获取枚举项的名称
	String toString()获取枚举项的名称
	<T> T valueOf(Class<T> type,String name) 用来获取指定的枚举项  参数1:枚举类对应的字节码对象 参数2 枚举项的名称
	values()  获取所有的枚举项
	此方法虽然在JDK文档中查找不到,但每个枚举类都具有该方法,它遍历枚举类的所有枚举值非常方便
B:案例演示:	枚举类的常见方法
public static void main(String[] args) {
	
	// 测试
	Direction front = Direction.FRONT ;
	Direction behind = Direction.BEHIND;
	Direction left = Direction.LEFT ;
	Direction right = Direction.RIGHT ;
	
	System.out.println(front.ordinal());
	System.out.println(behind.ordinal());
	System.out.println(left.ordinal());
	System.out.println(right.ordinal());
	
	System.out.println("----------------------------------");

	System.out.println(front.compareTo(right));
	
	System.out.println("----------------------------------");
	
	System.out.println(front.name());
	
	System.out.println("----------------------------------");
	
	System.out.println(front.toString());
	System.out.println(front);
	
	System.out.println("----------------------------------");
	
	// <T> T valueOf(Class<T> type,String name):	用来获取指定的枚举项
	// type: 表示的是对应的枚举的字节码文件对象
	// name: 就是枚举项的名称
	Direction direction = Direction.valueOf(Direction.class, "RIGHT") ;
	System.out.println(direction);
	
	System.out.println("----------------------------------");
	
	Direction[] directions = Direction.values() ;
	
	for(Direction d : directions){
		System.out.println(d);
	}

}

17.JDK7新特性

A:二进制字面量
	JDK7开始,终于可以用二进制来表示整数(byte,short,int和long)。
	使用二进制字面量的好处是,可以使代码更容易被理解。语法非常简单,只要在二进制数值前面加 0b或者0B
	int x = 0b110110
B:数字字面量可以出现下划线
	为了增强对数值的阅读性,如我们经常把数据用逗号分隔一样。JDK7提供了_对数据分隔。
	举例:
		int x = 100_1000;
	注意事项:
		不能出现在进制标识和数值之间
		不能出现在数值开头和结尾
		不能出现在小数点旁边
C:switch 语句可以用字符串
D:泛型简化
E:异常的多个catch合并
F:try-with-resources 语句
	try(必须是java.lang.AutoCloseable的子类对象){…}catch{...}
	好处:
		资源自动释放,不需要close()了
		把需要关闭资源的部分都定义在这里就ok了
		主要是流体系的对象是这个接口的子类(看JDK7的API)
发布了49 篇原创文章 · 获赞 9 · 访问量 1439

猜你喜欢

转载自blog.csdn.net/weixin_42401546/article/details/104866200