一、基本概念
(1)java类 用于描述一类事物的共性,只负责告诉我们这个类有什么属性,没有什么属性,至于属性的值是由实例对象来决定的。
(2)类对象和类的对象:类对象:类加载的产物,记录了类的信息。这个类是Class.
1、补充:
这就能过解释为什么强转的时候,编译器会知道,一个类的父类和子类是什么,因为信息被一个类记录了,根据信息,编译器就能过限制很多写法。
接口在强转中比较特殊,是因为如果是一个接口的话,编译器可能不会去找它的继承关系,都认为有类继承于它或者它实现别的类。
例子: class Student{}
interface snior extends Student{}//编译不通过 因为是接口 所以接口只能继承接口 不能继承普通类
这个波重要:这就是可以从技术上解释这个规范,剩下的就是从生活中解释规范
2、语法:
Class cls = 字节码;
可以解释编译的情况,编译的时候是会扫描字节码文件,如果不存在会报错。而负责这个的是系统组件ClassLoader,即当遇到一个未知的类时(系统没有,之前也没加载的)会生成字节码,然后后面的使用,才能拿到类的信息。
所以字节码文件存的是一个类的信息。 Class会创建一个引用指向它。
(3)获得类对象的方式:
1) 类名.class 直接获得类对象 还可以获得基本类型的类对象 double.class
2) 类的对象.getClass() 获得对应的类对象
3) Class.forName("类的全名") 通过类名获得类对象 两个意思:没有加载的要加载 加载的我要拿到
(4)常见方法
newInstance(): 通过类对象 创建类的对象 (调用类的无参构造方法)
代替了构造方法中的newInstance 简化了步骤
源代码也揭示了反射比较耗时,影响效率。
invoke():来自Method方法 对某对象调用该方法
Class.isPrimitive int.class = Integer.TYPE;这个对象是否是基本类型(原始类型)
int[].Class.isPrimitive 是对数组的类型判断 不是对数组的中的元素类型判断 所以这个方法的结果不是原始类型 因为数组是对象类型
int[].Class.isArray 判定原类型是否是数组
(5)9个预定义的的Class文件 8种基本类型和void类型
只要程序中出现的类型,都有各自的Class实例对象。例如int[],void
(6)反射
1、定义:将java类中的各种成分映射成相应的java类
2、应用:
(1)得到一个类的构造方法
String className = "java.lang.String";
Constructor[] constructors = Class.forName(className).getConstructors();
Constructor constructor = Class.forName(className).getConstructor(StringBuffer.class);
System.out.println(constructors.length);
System.out.println(constructor.toString());
Constructor constructor = Class.forName(className).getConstructor(2);//这句代码执行不通过 由于构造方法是并列的,不能按照顺序取出构造方法。
getConstructor()方法的参数列表形式:Class<?> parameterType
getConstructor(StringBuffer.class,int.class);//编译不通过,要求可变的参数的类型都是相同的
//构造的方法的newInstance()
String className = "java.lang.String";
Constructor constructor = Class.forName(className)
.getConstructor(StringBuffer.class);//选择StringBuffer
//构造方法
String s = (String) constructor.newInstance(
new StringBuffer("a"));//用这个对象的时候要
//传一个这个StringBuffer参数
//不好的地方 程序猿可能不知道constructor的所属是Data还是String
//的构造方法
(2)得到成员变量
class ReflectPoint{
private int x;
public int y;
public ReflectPoint(int x, int y) {
super();
this.x = x;
this.y = y;
}
}
public static void main(String[] args) throws Exception{
/*ReflectPoint rflctPoint = new ReflectPoint(1,2);
//得到字节码,拿到成员变量的信息
Class cls = rflctPoint.getClass();
//拿到要求的、特定的成员变量的信息
Field fld = cls.getField("y");
//根据拿到的信息,实例化一个信息
System.out.println(fld.get(rflctPoint));*/
//暴力反射访问私有
/*ReflectPoint r = new ReflectPoint(4,5);
Class cls = r.getClass();
Field fld = cls.getDeclaredField("x");//能够看到了 但拿不到
fld.setAccessible(true);//我要拿到它
System.out.println(fld.get(r));*///拿到了
}
//例子
public class practice {
public static void main(String[] args) throws Exception{
ReflectPoint f = new ReflectPoint(1,2);
changeValue(f);
System.out.println(f);
}
public static void changeValue (Object obj)throws Exception{
Field[] flds = obj.getClass().getDeclaredFields();
//System.out.println(flds.length);
for(Field fld:flds){
//System.out.println(fld.getType()==String.class);
if(fld.getType()==String.class){
String oldValue =(String)fld.get(obj);
String newValue = oldValue.replace('b', '2');
System.out.println(newValue);
fld.set(obj, newValue);
}
}
}
}
class ReflectPoint{
private int x;
public int y;
public String s1 = "abc";
public String s2 = "bcd";
public String s3 = "cde";
public ReflectPoint(int x, int y) {
//super();
this.x = x;
this.y = y;
}
@Override
public String toString() {
return "ReflectPoint [s1=" + s1 + ", s2=" + s2 + ", s3=" + s3 + ", x="
+ x + ", y=" + y + "]";
}
}
(3)方法
//得到String中CahrAt的方法
Method mthd = String.class.getMethod("charAt", int.class);
String s = "abc";
//再得到特定对象的CharAt的方法
System.out.println(mthd.invoke(s,1));//s如果是一个null表示调用的是静态方法
(4)对接收数组参数的成员方法进行反射
public class practice {
public static void main(String[] args) throws Exception{
//根据用户给我的类,我来调用它里面的main方法
//找到用户给我类的Class信息
String className = "test.Test";
Class cls = Class.forName(className);
//根据给的信息,找到需要的方法
Method mtd = cls.getMethod("main", String[].class);
//调用该方法
mtd.invoke(null, (Object)new String[]{"aaa","bbb","ccc"});
//mtd.invoke(null, new Object[]{new String[]{"aaa","bbb","ccc"}});
//mtd.invoke(null, new String[]{"aaa","bbb","ccc"});//这个方法不会出结果,因为为了兼容5.0一下版本,会将这个数组打散成三个元素,而main中的参数要求是一个对象类型 的单个元素。前两种方法是解决办法。
}
}
class Test{
public static void main(String[] args){
for(String arg:args){
System.out.println(arg);
}
}
}
其中的main方法不是static
public static void main(String[] args) throws Exception{
//对接收数组参数的成员方法进行反射
//根据用户给我的类,我来调用它里面的main方法
//找到用户给我类的Class信息
String className = "test.Test";
Class cls = Class.forName(className);
//根据给的信息,找到需要的方法
Object obj = cls.newInstance();
Method mtd = cls.getMethod("main", String[].class);
//调用该方法
mtd.invoke(obj, (Object)new String[]{"aaa","bbb","ccc"});
//mtd.invoke(null, new Object[]{new String[]{"aaa","bbb","ccc"}});
//mtd.invoke(null, new String[]{"aaa","bbb","ccc"});//这个方法不会出结果,因为为了兼容5.0一下版本,会将这个数组打散成三个元素,而main中的参数要求是一个对象类型 的单个元素。前两种方法是解决办法。
class Test{
public void main(String[] args){
for(String arg:args){
System.out.println(arg);
}
}
}
(5)数组与Object的关系及反射类型
数组的元素类型和纬度相同,那么这些数组的Class也相同
例子:int[] a1 = new int[3];
int[] a2 = new int[2];
int[][] a3 = new int[3][4];
String[] a4 = new String[3];
System.out.println(a1.getClass() == a2.getClass());
//System.out.println(a1.getClass() == a3.getClass());
//System.out.println(a1.getClass() == a4.getClass());
System.out.println(a1.getClass().getName());
System.out.println(a3.getClass().getName());
System.out.println(a4.getClass().getName());
System.out.println(a1.getClass().getSuperclass().getName());
System.out.println(a1.getClass().getSuperclass().getName());
Object o1 = a1;
Object o2 = a3;
Object o3 = a4;
//Object[] o3 = a1;//报错
Object[] o5 = a3;
Object[] o6 = a4;
//这个地方是比较奇特的。。。。用数组对象的引用指向一个数组对象 而不是用一个对象引用指向数组对象
看这个问题的应用:
//这个地方的应用可以解释这个下面现象,当a1是int时,不会使用4.0的方式
//就是参数不是数组。5.0参数可以变化 String就可以打印出来
System.out.println(Arrays.asList(a1));
System.out.println(Arrays.asList(a4));
System.out.println(Arrays.asList(a3));
(6)数组的反射应用
用反射的方式操作数组,例如:我们常要遍历数组,或者更改某一个数组元素的值,或者取出某个特定下标的元素。
//数组的反射应用
public void printObject(Object obj){
Class cls = obj.getClass();
if(cls.isArray()){//判断是否数组
//遍历数组
//Array利用反射操作数组的工具类
int length = Array.getLength(obj);
for(int i=0;i<length;i++){
System.out.println(Array.get(obj, i));
}
}else{
System.out.println(obj);
}
}
利用反射得到数组的类型的一个问题:
Object[] objs = new Object[]{"1",1};
类型是什么?
Object[] objs = new Object[]{"1",1};
Object obj = objs[0].getClass().getName();
//String
Object obj = objs[1].getClass().getName();
//Integer
System.out.println(obj);
数组之前所学说,是同类型的元素放到一起,现在看,这个就有问题了。
(7)框架的概念及用反射技术开发框架的原理
我们使用的类有两种:我们使用别人的:工具类。别人使用我们的:框架。
框架要解决的问题:我们写的框架,可以调用以后人们写的类。
1、加载配置文件
2、生成实例newInstance()
package test;
import java.util.*;
import java.io.*;
public class TestReflection {
public void reflectionApp() throws Exception{
InputStream ips = new FileInputStream("config.propertis");
Properties props = new Properties();
props.load(ips);
ips.close();
String className = props.getProperty("className");
Collection<ReflectionPoint> collections = (Collection)Class.forName(className).newInstance();
ReflectionPoint rfctp1 =new ReflectionPoint(3,3);
ReflectionPoint rfctp2 =new ReflectionPoint(3,4);
ReflectionPoint rfctp3 =new ReflectionPoint(3,5);
ReflectionPoint rfctp4 =new ReflectionPoint(3,3);
collections.add(rfctp1);
collections.add(rfctp2);
collections.add(rfctp3);
collections.add(rfctp4);
collections.add(rfctp1);
System.out.println("ArrayList");
for(ReflectionPoint rfctp:collections){
System.out.println(rfctp);
}
collections = new HashSet<ReflectionPoint>();
collections.add(rfctp1);
collections.add(rfctp2);
collections.add(rfctp3);
collections.add(rfctp4);
collections.add(rfctp1);
System.out.println("HashSet");
for(ReflectionPoint rfctp:collections){
System.out.println(rfctp);
}
}
}
class ReflectionPoint{
//属性
private double x;
private double y;
//构造方法
public ReflectionPoint(){}
public ReflectionPoint(double x,double y){
this.x = x;
this.y = y;
}
//set和get方法
public double getX(){return x;}
public double getY(){return y;}
public void setX(double x){this.x = x;}
public void getY(double y){this.y = y;}
//toString
public String toString(){
return "X = " + x + " Y = " + y;
}
//equals
public boolean equals(Object o){
/*if(this == o) return true;
if(o == null) return false;
if(this.getClass()!=o.getClass())return false;
ReflectionPoint rf = (ReflectionPoint)o;
if(rf.getX()!=x)return false;
if(rf.getY()!=y)return false;
if(rf.getX()!=x)return false;
if(rf.getY()!=y)return false;*/
System.out.println(" equals "+x+"+"+y);
return true;
}
//hashcode
public int hashCode(){
return (int)x;
}
}
(8)用类加载器的方法管理资源和配置文件
配置文件存放路径:当前工作路径。
解决配置文件存放路径遇到的问题:用户将信息存放在配置文件中,程序是通过get配置文件信息的方式来找到配置文件。简单来说就是:配置文件管理配置文件。
例子:得到项目的绝对路径之后找到项目中的配置文件。即:绝对路径+相对路径。
应用:将配置文件放到统一的目录中。
类加载器:可以加载类也可以加载类的配置文件信息。
ClassLoader.getResourceAsStream(String ProjectPath);
ProjectPath = "it\cast\day1\config.properties";
Class.getResourceAsStream(String ProjectParh);
ProjectPath = "config.properties";知道是在我的包里的。
ProjectPath = "it\cast\day1\config.properties";也可以用这个路径,我会判断是否是在我的包里。 框架中都是将配置文件放到ClassPath中,就是因为内部的机制是使用类加载器加载的配置文件