黑马程序员—java核心技术—反射

                                                                           ------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

 

反射这部分内容,是java部分的核心技术,重中之重,所以一定要学好。难点是对动态代理的理解:什么叫反射呢?反射也叫反向加载。我们正常创建一个对象是先new一个实例出来,而反射则是不通过创建实例对象,直接通过.class文件反向加载访问该类中的属性和方法。

获取class对象的三种方式:

1.调用Object类的getClass():任何类都会继承此方法;
2.任何的数据类型(包括基本类型)都有一个:静态的class属性:
  Student.class;
  int.class
3.调用Class类的静态方法forName(String 全名限定的类名):(常用)

通过这个Class对象,可以获取Student类内部的成员属性、构造方法、成员方法的一些信息,
并能够调用它们;
伪代码:

Student stu = new Student();
  
Class stuClass1 = stu.getClass();
  
Class stuClass2 =Student.class;
  
Class stuClass3 = Class.forName("cn.itcast.demo01_获取Class对象的三种方式.Student");

如何获取构造方法:

获取构造方法,并调用:

Class类:
 --批量的:
  Constructor[] getConstructors() :获取所有的"公有构造方法"
  Constructor[] getDeclaredConstructors() :获取全部(包括私有)的构造方法;
 --获取某个构造方法
  Constructor getConstructor(Class ... parameterTypes) :获取指定的"公有构造方法";
  Constructor getDeclaredConstructor(Class ... parameterTypes) :获取指定的构造方法(包括私有的);
 
 --调用某个构造方法:
  Constructor的 Object newInstance(Object... initargs) :调用指定构造方法,并实例化此类的对象;
 
 --暴力访问:如果私有成员,需要设置暴力访问;
  Constructor的setAccessible(true):不进行权限检查;
示例:

public class Demo {
	public static void main(String[] args) throws Exception {
		//1.获取Student的Class对象
		Class stuClass = Class.forName("cn.itcast.demo02_通过Class对象获取构造方法并调用.Student");
		System.out.println("************获取所有的\"公有构造方法\"*******************");
		Constructor[] conArray = stuClass.getConstructors();
		for(Constructor c : conArray){
			System.out.println(c);
		}
		
		System.out.println("************获取所有的构造方法(包括私有的)***************");
		conArray = stuClass.getDeclaredConstructors();
		for(Constructor c : conArray){
			System.out.println(c);
		}
		
		System.out.println("************获取\"公有构造方法\"***********************");
		Constructor con = stuClass.getConstructor(String.class);//获取的是"具有String类型形参的"公有构造方法
	//	Constructor con = stuClass.getConstructor(String.class,int.class);
		Object obj = con.newInstance("刘德华");//调用此构造方法,并传递实参:相当于:Object obj = new Student("刘德华");
		
		System.out.println("************获取\"私有构造方法\"并调用*******************");
		con = stuClass.getDeclaredConstructor();
		//设置暴力访问
		con.setAccessible(true);
		
		obj = con.newInstance();
		
	}
}

获取成员属性:

import java.lang.reflect.Field;

/*
 * 获取成员属性,并调用:
 * 
 * Class类的方法:
 * 		--批量的:
 * 			Field[] getFields():获取所有"公有属性";
 * 			Field[] getDeclaredFields() :获取所有成员属性(包括私有):
 * 		--获取单个成员属性
 * 			Field getField(String name) :获取指定的"公有属性";
 * 			Field getDeclaredField(String name) :获取指定的属性,包括私有的;
 * 
 * 		--为成员属性赋值:(注意:1.一定要先实例化此类对象;2.访问私有属性前,要设置暴力访问)
 * 			Field的 void set(Object obj, Object value) 
 
 */
public class Demo {
	public static void main(String[] args) throws Exception {
		//1.获取Student的Class对象
		Class stuClass = Class.forName("cn.itcast.demo03_通过Class对象获取成员属性并调用.Student");
		System.out.println("********************获取所有\"公有属性\"************************");
		Field[] fieldArray = stuClass.getFields();
		for(Field f : fieldArray){
			System.out.println(f);
		}
		System.out.println("********************获取所有属性(包括私有的)************************");
		fieldArray = stuClass.getDeclaredFields();
		for(Field f : fieldArray){
			System.out.println(f);
		}
		
		//创建一个Student对象
		Object obj = stuClass.getConstructor().newInstance();
		System.out.println("*********************获取公有属性,并调用*********************************");
		Field f = stuClass.getField("name");
		System.out.println("赋值前打印:" + obj);
		f.set(obj, "刘德华");
		System.out.println("赋值后打印:" + obj);
		
		System.out.println("*********************获取私有属性,并赋值*********************************");
		f = stuClass.getDeclaredField("score");
		//暴力访问
		f.setAccessible(true);
		
		f.set(obj, 88.5);
		System.out.println(obj);

	}
}

通过class获取成员方法并调用:

import java.lang.reflect.Method;

/*
 * 获取成员方法:
 * 
 * Class类的方法:
 * 		--批量的:
 * 			Method[] getMethods():获取所有"公有方法"(包括继承的)
 * 			Method[] getDeclaredMethods() :获取所有成员方法(包括私有):
 * 		--获取单个成员方法
 * 			Method getMethod(String name, Class... parameterTypes) :获取指定的"公有方法";
 * 			Method getDeclaredMethod(String name, Class... parameterTypes) :获取指定的方法,包括私有的;
 * 
 * 		--调用方法:(注意:1.要先创建此类的对象;2.调用私有方法前,要先设置暴力访问)
 * 			Method:
 * 				Object invoke(Object obj, Object... args) :调用Method所代表的方法:
 * 				返回值:此Method对象调用所代表的方法,所获取的返回值;
 * 				形参:obj:方法所属对象;
 * 					args:Method所代表的那个方法的实参列表;
 * 				
 */
public class Demo {
	public static void main(String[] args) throws Exception {
		//1.获取Class对象
		Class stuClass = Class.forName("cn.itcast.demo04_通过Class对象获取成员方法并调用.Student");
		System.out.println("*************获取所有\"公有方法\"*******************");
		Method[] methodArray = stuClass.getMethods();
		for(Method m : methodArray){
			System.out.println(m);
		}
		System.out.println("**************获取所有的方法(包括私有的)***************");
		methodArray = stuClass.getDeclaredMethods();
		for(Method m : methodArray){
			System.out.println(m);
		}
		
		//创建Student对象
		Object obj = stuClass.getConstructor().newInstance();
		
		System.out.println("**************获取公有的,带参的show1()方法,并调用******");
		Method m = stuClass.getMethod("show1", String.class,int.class);
		Object result = m.invoke(obj, "刘德华",20);//调用show1()方法
		System.out.println("调用的返回值:" + result);
		
		System.out.println("***************获取私有的,无参的、无返回值的show4()方法,并调用****************");
		m = stuClass.getDeclaredMethod("show4");
		m.setAccessible(true);
		m.invoke(obj);
		
		
	}
}

反射的重要应用,操作配置文件:日常的软件中,我们需要更新软件,加入在主方法里应用对象1 ,对象1 更新了,出现了对象2,我们还要在主方法里修改代码,这样不仅费时费力,还浪费资源,怎么可以实现不修改源代码情况下的更新呢?这个时候就可以利用反射机制实现操作配置文件了,配置文件为xxx.ini。里面是键值对形式存在的

A=A1

B=B1

每次程序更新,只要修改配置文件就可以了。利用键的获取值。

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Properties;

public class Demo {
	public static void main(String[] args) throws Exception {
		//1.获取Student的Class对象
	//	Class stuClass = Class.forName("cn.itcast.demo05_练习_通过反射运行配置文件内容.Student");
		String classPath = getValue("className");
		Class stuClass = Class.forName(classPath);
		
		//2.实例化一个Student对象
		Object obj = stuClass.getConstructor().newInstance();
		//3.调用方法
	//	Method m = stuClass.getMethod("show");
		Method m = stuClass.getMethod(getValue("methodName"));
		m.invoke(obj);
		
	}
	
	//一个方法,获取配置文件中的某个键对应的值
	public static String getValue(String key) throws IOException{
		Properties pro = new Properties();
		FileReader in = new FileReader("properties.ini");
		pro.load(in);
		in.close();
		
		return pro.getProperty(key);
	}
}	

通过反射越过泛型检查:分析,我们知道,泛型是只存在于编译期的,而反射是直接操作.class文件的,所以,利用java语言的反射机制,可以越过集合的泛型检查。

案例:

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/*
 * 通过反射越过泛型检查:
 * 
 * 1.定义一个具有String泛型的集合,要求成功向集合中添加一个int数据;
 */
public class Demo {
	public static void main(String[] args) throws Exception, SecurityException {
		List<String> strList = new ArrayList<>();
		strList.add("aaa");
		strList.add("bbb");
	//	strList.add(10);
		
		//通过反射
		Class listClass = strList.getClass();
		//获取add方法
		Method addMethod = listClass.getMethod("add", Object.class);
		//调用add方法
		addMethod.invoke(strList, 10);
		
		//遍历集合
		for(Object o : strList){
			System.out.println(o);
		}
		
	}
}

练习:通过反射写一个方法,能够设置某个对象的某个属性为指定的值

import java.lang.reflect.Field;

/*
 * 通过反射写一个通用的方法,能够设置某个对象的某个属性为指定的值
 */
public class Demo {
	public static void main(String[] args) throws SecurityException, Exception {
		Cat c = new Cat();
		setValue(c, "name", "波斯猫");
		c.show();
	}
	//通过反射写一个通用的设置某个对象的某个属性为指定的值
	public static void setValue(Object target,String fieldName,Object value) throws Exception, SecurityException{
		//1.获取Class对象
		Class aClass = target.getClass();
		//2.获取字段
		Field f = aClass.getDeclaredField(fieldName);
		//3.暴力访问
		f.setAccessible(true);
		//4.设置
		f.set(target, value);
	}
}<pre class="java" name="code">public class Cat {
	private String name;
	
	public void show(){
		System.out.println("name = " + name);
	}
}
public class Cat {
	private String name;
	
	public void show(){
		System.out.println("name = " + name);
	}
}

代理模式:代理模式,就是为锁代理的类增加一些额外的操作:

简单实现:

/*
 * 此类来代理Student类
 * 代理类的目的:是为所代理的类的某些方法,增加一些额外的操作;
 */
public class StudentProxy {
	private Student stu = new Student();
	public void coding(){
		check();
		stu.coding();
		zongJie();
	}
	
	private void check(){
		System.out.println("前期检查......");
	}
	private void zongJie(){
		System.out.println("后期总结......");
	}
}
public class Student {
	public void coding(){
		System.out.println("我写程序......");
	}
}
public class Demo {
	public static void main(String[] args) {
//		Student stu = new Student();
		
		StudentProxy stu = new StudentProxy();
		stu.coding();
	}
}

这样的代理模式,由于每增加一个新产品,都要必须添加一个新的代理类,增加了代码数量,基于此,java反射提供了动态代理机制:

import java.lang.reflect.Proxy;

/*
 * Java中实现动态代理:
 * 
 * 步骤:
 * 1.自定义类,实现InvocationHandler接口;
 * 	 重写invoke()方法:需要代理的方法,都要经过此方法,
 *   我们可以在此方法中做我们想要做的事情;
 * 2.动态生成代理对象:
 * 	  Proxy类静态方法:newProxyInstance()
 * 
 * 注意:Java的动态代理机制是基于"接口"的,也就是所有需要被代理的类都必须实现某个接口,
 *      以达到它们作为共同类型的目的;
 */
public class Demo {
	public static void main(String[] args) {
		IDAO stuDao = (IDAO)Proxy.newProxyInstance(StudentDAO.class.getClassLoader(),
								StudentDAO.class.getInterfaces(), 
								new MyInvocationHandler(new StudentDAO()));
		stuDao.save();
		stuDao.update();
		
		IDAO teaDao = (IDAO)Proxy.newProxyInstance(TeacherDAO.class.getClassLoader(), 
													TeacherDAO.class.getInterfaces(), 
														new MyInvocationHandler(new TeacherDAO()));
		teaDao.save();
		teaDao.update();
		
		
	}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class MyInvocationHandler implements InvocationHandler {
	private Object target;
	public MyInvocationHandler(Object target){
		this.target = target;
	}
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		check();
		Object result = method.invoke(target);//这一步才是真正调用被代理对象中的方法;
		log();
		return result;
	}

	public void check(){
		System.out.println("权限检查......");
	}
	public void log(){
		System.out.println("记录日志......");
	}
}

模板设计:解决子类重写方法时候权限过大的问题:

/*
 * 模版设计模式概述和使用
 * 
 * 
 */
abstract class Person{
	public void show(){//模板方法
		System.out.println("大家好,我是黑马的一名 " + getType() + "!");
	}
	public abstract String getType();
}
class Student extends Person{
	public String getType(){
		return "学生";
	}
}
class Teacher extends Person{
	public String getType(){
		return "教师";
	}
}
public class Demo {
	public static void main(String[] args) {
		Student stu = new Student();
		stu.show();
		
		Teacher tea = new Teacher();
		tea.show();
	}
}

装饰设计:代码量过大,不写代码了,简单说明一下,装饰设计模式,和代理模式很像,但又不是代理模式,装饰模式的结构图如下:

 

枚举实际上是基于多利模式的,就比如拿颜色来举例:

自己实现枚举类:

1.多例模式:某些类在整个应用程序运行期间,只需要几个对象,不能多,有时也不能少;
      例如:扑克类--只能有54个对象;
           骰子类--只能有2个对象;
2.枚举,就是基于"多例模式";
3.例子:我们的程序需要颜色类,我们定义一个类MyColor。在整个应用程序运行期间,只需要
      三个对象:红,绿,蓝
     
4.枚举的实现方式:
  1).定义MyColor类,不能让其它类任意的实例化此类对象--构造方法私有化
枚举的关键字:enum;

/*
 * 通过enum实现枚举类
 * 
 * 1.enum是Java中第一个关键字,用于定义"枚举";
 * 
 * 枚举的注意事项:
 * 	定义枚举类要用关键字enum:
	所有枚举类都是Enum的子类
	枚举类的第一行上必须是枚举项,最后一个枚举项后的分号是可以省略的,但是如果枚举类有其他的东西,这个分号就不能省略。建议不要省略
	枚举类可以有构造器,但必须是private的,它默认的也是private的。枚举项的用法比较特殊:枚举(“”);
	枚举类也可以有抽象方法,但是枚举项必须重写该方法
	

 */
public class Demo {
	public static void main(String[] args) {
		MyColor redColor = MyColor.RED;
		System.out.println(redColor);
		redColor.show();
	}
}
public enum MyColor {
	RED("红"){
		@Override
		public void show() {
			System.out.println("我是:" + this.getName() + " 色的!");
		}
	},GREEN("绿"){
		@Override
		public void show() {
			System.out.println("我是:" + this.getName() + " 色的");
		}
	},BLUE("蓝"){
		@Override
		public void show() {
			System.out.println("我是:" + this.getName() + " 色的");
		}
	};
	private String name;
	private MyColor(String name){
		this.name = name;
	}
	
	public String getName(){
		return this.name;
	}
	//包含抽象方法
	public abstract void show();
	//重写toString
	@Override
	public String toString() {
		return "MyColor [name = " + this.name + "]";
	}
}

枚举中常用的一些方法:

/*
 * 枚举中常用的方法:
 * 
 * 	int compareTo(E o):比较的是序号;
	String name():返回枚举的名称;
	int ordinal():获取序号
	String toString():默认打印枚举名称,可以重写;
	<T> T valueOf(Class<T> type,String name):将字符串转换为某个枚举类型;
	values() :将枚举转换为数组;

 */
public class Demo {
	public static void main(String[] args) {
		MyColor red = MyColor.RED;
		MyColor green = MyColor.GREEN;
		MyColor blue = MyColor.BLUE;
		
		System.out.println(red.compareTo(green));
		System.out.println(red.compareTo(blue));
		System.out.println(blue.compareTo(red));
		System.out.println(green.compareTo(red));
		
		System.out.println("red.name():" + red.name());
		
		System.out.println(red.ordinal());
		System.out.println(green.ordinal());
		System.out.println(blue.ordinal());
		
		MyColor c = MyColor.valueOf(MyColor.class, "BLUE");
		System.out.println(c);
		System.out.println("将枚举转换为数组:");
		MyColor[] colorArray = MyColor.values();
		for(MyColor cc : colorArray){
			System.out.println(cc);
		}
		
	}
}

java基础知识新特性回归:

到这里,所有的java基础知识都已经学完了,jdk5,jdk7都有一些新的特性,现在进行一下新特性总结:

jdk5新特性:

import java.util.ArrayList;
import java.util.List;

/*
 * JDK5的新特性:
 * 	1.自动装箱和拆箱
	2.泛型
	3.增强for循环
	4.静态导入
	5.可变参数
	6.枚举

 */
//import static java.lang.Math.max;
import static java.lang.Math.*;
public class Demo {
	public static void main(String[] args) {
		//1.自动装箱和拆箱
		Integer intObj = 10;//自动装箱
		int n = intObj;//自动拆箱
		intObj++;//先拆箱,后装箱
		//2.泛型
		List<String> strList = new ArrayList<>();
		strList.add("aaa");
	//	strList.add(10);//编译错误
		//3.增强for
		int[] intArray = {1,32,432,54,41,41};
		for(int n2 : intArray){
			System.out.println(n2);
		}
		
		//4.静态导入:
		System.out.println("求10,20的最大值:" + max(10, 20));
		//5.可变参数
		show();//可以不传参数
		show(1,432,43,25,3424,423,342,432,43,243,24,325,43654,76,54);
		
	}
	//可变参数
	public static void show(int ... nums){
		for(int n : nums){
			System.out.println(n);
		}
	}
}


jdk7的新特性:

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/*
 * JDK7的六个新特性回顾:
 * 
 * 	1.二进制字面量
	2.数字字面量可以出现下划线
	3.switch 语句可以用字符串
	4.泛型简化
	5.异常的多个catch合并
	6.try-with-resources 语句

 */
public class Demo {
	public static void main(String[] args) {
		//1.二进制字面量
		System.out.println(10);//十进制
		System.out.println(0b1010);//二进制
		System.out.println(012);//八进制
		System.out.println(0xA);//十六进制
		//2.数字字面量可以出现下划线
		double n = 1_2_3_4_5_6.7_8_9;
		System.out.println(n);
		//3.switch 语句可以用字符串
		String str = "JAVA";
		switch(str){
			case "Java":
			case "JAVA":
			case "java":
				System.out.println("欢迎学习Java");
				break;
			case "C++":
				System.out.println("欢迎学习C++");
				break;
			case "C#":
				System.out.println("欢迎学习C#");
				break;
		}
		//4.泛型简化
		List<String> list = new ArrayList<>();
		
		//5.异常的多个catch合并
		try{
			
		}catch(ArrayIndexOutOfBoundsException | NullPointerException | ArithmeticException   变量名){
			
		}catch(Exception e){
			
		}
		
		//6.try-with-resources 语句
		
		try(
				FileInputStream in = new FileInputStream("demo17.txt");
		){
			
		}catch(FileNotFoundException e){
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
}

以上到此,就是全部的java基础的主要知识,非常期待能够进入黑马训练营继续学习编程!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值