java通常是先有类再有对象,有对象我就可以调用方法或者属性。反射其实是通过Class对象来调用类里面的方法。通过反射可以调用私有方法和私有属性。大部分框架都是运用反射原理
一、Class类的使用
1.在面向对象的世界中,完事万物都是对象
2.java语言中静态成员和普通数据类型不是对象
3.类也是对象,是java.lang.Class的实例对象
- 任何一个类都是Class的实例对象,有三种表示方式:
Class c1 = myClass.class;
Class c2 = myObject.getClass();
Class c3 = Class.forName("com.myClass");
class Foo{
public void print()
{
System.out.println("it is Foo");
}
}
public class ClassDemo1 {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
//Foo类的实例对象
Foo foo1 = new Foo(); //foo1表示Foo类的实例对象
/*
* Foo这个类本身也是一个实例对象,就是Class(java.lang.Class)类的实例对象
* /任何一个类都是Class类的实例对象, 该实例对象有三种表示方法
*/
//第一种 --> 任何一个类都有一个隐含的静态成员变量class
Class c1 = Foo.class; //c1表示Class类的实例对象
//第二种,已知该类的对象, 通过该类对象的getClass方法表示
Class c2 = foo1.getClass(); //c2表示Class类的实例对象
/*
* 以上c1,c2都表示Class类的实例对象,但是这个实例对象又是说Foo这个类
* c1,c2表示Foo类的类类型 ( class type ), 因为Foo类可以理解为Class类的实例对象
* 也就是世界万物皆对象,类也是对象,是Class类的实例对象
* 这个对象我们称为该类的类类型
*/
//不管c1 or c2都代表了Foo类的类类型,一个类只可能是Class类的一个实例对象, 所以c1=c2
System.out.println(c1==c2);//输出: true
//第三种
Class c3 = null;
try {
c3 = Class.forName("com.lxf.reflect.Foo");
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(c3==c2); //输出:true
//我们可以通过类的类类型创建该类的实例--->通过c1 or c2 or c3创建Foo的实例
try {
Foo foo2 = (Foo)c2.newInstance(); //需要有无参数的构造方法
foo2.print();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
二.静态加载
- 在编译java源文件的时候的加载类叫做静态加载,比如:Test.java文件如下
class Test{
public static void main(String[] args) {
People p = new People();
p.eat();
}
}
编译Test.java
javac Test.java
这时候People.class字节码文件并没有,所以在编译的时候会报错
创建People.java
class People
{
public void eat()
{
System.out.println("我喜欢美食");
}
}
此时在编译
先编译People.java
javac People.java //编译后会产生People.class字节码文件
javac Test.java //编译后会产生Test.class字节码文件
执行Test
java Test //会输出:我喜欢美食
三、动态加载
通过Class a=Class.forName(arg[0]);此时为动态加载,因为编译时不知道使用哪个类,因此编译没有加载任何类,通过编译。运行时,根据 Javac office.java word (word为arg[0],也是类类型),去确定a是哪个类。这就是动态加载。
实际问题举例:
class offic
{
public static void main(String[] args)
{
if("Word".equals(args[0]))
{
Word w=new Word();
w.start();
}
if("Excel".equals(args[0])
{
Excel e=new Excel();
e.start();
}
}
}
编译无法通过,Word类、Excel类和start()方法不存在。
class Word
{
public static void start(){
System.out.println("Word.....start");
}
}
创建Word类后,进行编译,仍会报错,Excel类不存在。如果我们只想用已经存在的Word类,此类方法也是无法实现的。因为new创建的对象是静态加载类,在编译时刻需要加载所有可能使用的类,不管你是否会用到。
class OfficeBetter
{
public static void main(String[] args)
{
try
{
//动态加载类
Class c=Class.forName(args[0]);
//通过类类型创建该类对象
OfficeAble oa=(OfficeAble)c.newInstance();
oa.start();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
通过动态加载,编译过程中不会报错。编写借口OfficeAble直接创建对象,Word类和Excel类通过继承方式,实现不同方法。
interface OfficeAble
{
public void start();
}
class Word implements OfficeAble
{
public void start(){
System.out.println("Word.....start");
}
}
- 基本数据类型,void关键字都存在类类型
四、获取信息
- 1、获取方法信息
Method类,方法对象,一个成员方法就是一个Method对象
getMethods()方法获取的是所有public函数,包括父类继承而来的
getDeclaredMethods()获取的是所有该类自己声明的方法,不问访问权限
/**
* 打印类的信息,包括类的成员函数、成员变量(只获取成员函数)
* @param obj 该对象所属类的信息
*/
public static void printClassMethodMessage(Object obj){
//要获取类的信息 首先要获取类的类类型
Class c = obj.getClass();//传递的是哪个子类的对象 c就是该子类的类类型
//获取类的名称
System.out.println("类的名称是:"+c.getName());
/*
* Method类,方法对象
* 一个成员方法就是一个Method对象
* getMethods()方法获取的是所有的public的函数,包括父类继承而来的
* getDeclaredMethods()获取的是所有该类自己声明的方法,不问访问权限
*/
Method[] ms = c.getMethods();//c.getDeclaredMethods()
for(int i = 0; i < ms.length;i++){
//得到方法的返回值类型的类类型
Class returnType = ms[i].getReturnType();
System.out.print(returnType.getName()+" ");
//得到方法的名称
System.out.print(ms[i].getName()+"(");
//获取参数类型--->得到的是参数列表的类型的类类型
Class[] paramTypes = ms[i].getParameterTypes();
for (Class class1 : paramTypes) {
System.out.print(class1.getName()+",");
}
System.out.println(")");
}
}
- 2、获取成员变量信息
成员变量是java.lang.reflect.Field的对象
1、Field类封装了关于成员变量的操作
2、Field[] fs = c.getFields()方法获取所有public的成员变量Field[]信息
3、c.getDeclaredFields获取的是该类自己声明的成员变量信息
4、field.getType()获得成员类型的类类型
5、field.getName()获得成员的名称
/**
* 获取成员变量的信息
* @param obj
*/
public static void printFieldMessage(Object obj) {
Class c = obj.getClass();
/*
* 成员变量也是对象
* java.lang.reflect.Field
* Field类封装了关于成员变量的操作
* getFields()方法获取的是所有的public的成员变量的信息
* getDeclaredFields获取的是该类自己声明的成员变量的信息
*/
//Field[] fs = c.getFields();
Field[] fs = c.getDeclaredFields();
for (Field field : fs) {
//得到成员变量的类型的类类型
Class fieldType = field.getType();
String typeName = fieldType.getName();
//得到成员变量的名称
String fieldName = field.getName();
System.out.println(typeName+" "+fieldName);
}
}
- 3、获取构造方法信息
构造函数是java.lang.Constructor类的对象
1、通过Class.getConstructor()获得Constructor[]所有公有构造方法信息
2、建议getDeclaredConstructors()获取自己声明的构造方法
3、Constructor.getName():String
4、Constructor.getParameterTypes():Class[]
/**
* 打印对象的构造函数的信息
* @param obj
*/
public static void printConMessage(Object obj){
Class c = obj.getClass();
/*
* 构造函数也是对象
* java.lang. Constructor中封装了构造函数的信息
* getConstructors获取所有的public的构造函数
* getDeclaredConstructors得到所有的构造函数
*/
//Constructor[] cs = c.getConstructors();
Constructor[] cs = c.getDeclaredConstructors();
for (Constructor constructor : cs) {
System.out.print(constructor.getName()+"(");
//获取构造函数的参数列表--->得到的是参数列表的类类型
Class[] paramTypes = constructor.getParameterTypes();
for (Class class1 : paramTypes) {
System.out.print(class1.getName()+",");
}
System.out.println(")");
}
}
JAVA中extends 与implements有啥区别?
- 在类的声明中,通过关键字extends来创建一个类的子类。一个类通过关键字implements声明自己使用一个或者多个接口。
extends 是继承某个类, 继承之后可以使用父类的方法, 也可以重写父类的方法; implements 是实现多个接口, 接口的方法一般为空的, 必须重写才能使用
2.extends是继承父类,只要那个类不是声明为final或者那个类定义为abstract的就能继承,JAVA中不支持多重继承,但是可以用接口 来实现,这样就要用到implements,继承只能继承一个类,但implements可以实现多个接口,用逗号分开就行了
五、方法的反射
1、获得类类型 A a1 = new A(); Class c= a1.getclass;
2、获得方法对象 Method getMet=c.getMethod("print",String.class,String.class);//忘了加引号 其中可以用new Class[]{}数组作为参数
3、反射,m.invoke(调用方法的对象,参数); 参数可以 new Object[]{}
4、反射结果和直接对象调用结果一样,本例都是 打印输出
六、Java反射机制
——通过反射了解集合泛型的本质
1:反射的操作都是编译之后的操作;就是运行阶段
2:java中集合的泛型是防止错误输入的;只在编译阶段有效,只要绕过编译就无效啦
1:注解是一个什么东西——一种技术,使用特定语法,能过比较简单的实现一些比较牛逼的功能
2:注解的功能什么?也就是都有什么类型的注解?注解都能干什么?还是说什么都能干?
3:有些注解是人家提供的,如果想自己编写注解,该怎么玩?然后就是什么情况下自己编写注解比较好?怎么才能又快又好的编写自己的注解?
4:想了解一下,框架中注解是怎么使用的比如:Spring框架
七、Java自带的常用注解:
- @Overrid (覆盖)覆盖了父类的方法
- @Deprecated(弃用)过时了的方法,不能删掉,有可能导致错误,所以注解成过时。
- @Suppvisewarnings(忽略)调用deprecated方法会导致警告,使用@Suppvisewarning("deprecation")注解会忽略掉这个警告
常见第三方注解
Spring:@Autowired @Service @Repository
- @Autowired:可以对成员变量、方法和构造函数进行标注,来完成自动装配的工作
用法:
public class UserManagerImpl implements UserManager {
@Autowired
private UserDao userDao;
}
- @Service:定义某个类为一个bean,则在这个类的类名前一行使用@Service("XXX"),就相当于讲这个类定义为一个bean,bean名称为XXX。而无需去xml文件内去配置。
- 源码注解:只存在于源代码中,编译后就没有了。
- 编译时注解:存在源代码和.class文件中
- 注解的分类:
按照运行机制分类:源码注解;编译时注解;运行时注解。
按照来源分类:来自JDK(如override);来自第三方(如spring的autowired);自定义注解。 - 元注解:给注解的注解。
- 自定义注解的语法
(1)使用@interface关键字定义注解
(2)无参无异常声明成员
(3)可用default为成员指定一个默认值
(4)合法成员类型,包括:原始、String、Class、Annotation和Enumeration
(5)如只有一个成员,则成员名必须为value(),且使用时可忽略"成员名="
(6)无成员的注解称为标识注解 - 元注解:注解自定义注解的注解
@Target(作用范围):使用ElementType增加范围
Annotation可被用于 packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数)
1.CONSTRUCTOR:用于描述构造器
2.FIELD:用于描述域
3.LOCAL_VARIABLE:用于描述局部变量
4.METHOD:用于描述方法
5.PACKAGE:用于描述包
6.PARAMETER:用于描述参数
7.TYPE:用于描述类、接口(包括注解类型) 或enum声明
@Rentention(作用时长):使用RetentionPolicy限制
1.SOURCE:在源文件中有效(即源文件保留)
2.CLASS:在class文件中有效(即class保留)
3.RUNTIME:在运行时有效(即运行时保留)
@Inherited(可继承性):允许子类继承
如果继承的是借口,注释无法被继承;如果继承的时类,则只有类上的注释可以被继承,方法上的注释无法被继承。
@Documented(生成文档):可生成在JavaDoc文档
如果注解只有一个成员,则成员名必须取名为value(),在使用时可以忽略成员名和赋值号(=),这是一个约定俗成的规则,不这样使用也可以编译通过。
解析注解: