Без лишних слов, давайте сразу к делу. Давайте сначала сфотографируем механизм отражения -->
когнитивный рефлекс
- Во-первых, мы понимаем отражение через "анти". Раз есть "анти", то должно быть и "положительное". В нормальных условиях мы думаем, что сначала должны быть классы, а потом объекты.следующее:
import java.util.Date;//先有类
public class ReflectTest1 {
public static void main(String[] args) {
Date date = new Date();//后有对象
System.out.println(date);
}
}
- Так называемый «обратный» относится к использованию объекта для поиска источника объекта. В классе Object есть метод:
пример:соблюдать "анти"
import java.util.Date;
public class ReflectTest2 {
public static void main(String[] args) {
Date date = new Date();
System.out.println(date.getClass());
}
}
"class java.util.Date"
Найдите результат после вызова метода getClas() и выведите полное имя класса, что эквивалентно поиску источника объекта.
- Создание объекта класса
java.lang.Class — это класс, этот класс является источником операций отражения, то есть: все отражения должны начинаться с класса, и самое главное, что у этого класса есть три метода инстанцирования:
1.Вызовите метод getClass() в классе Object:
import java.util.Date;
public class ReflectTest3 {
public static void main(String[] args) {
Date date = new Date();
Class<?> cls = date.getClass();
System.out.println(cls);
}
}
"class java.util.Date"
2. Используйте «class.class», чтобы получить:
import java.util.Date;
public class ReflectTest4 {
public static void main(String[] args) {
Class<?> cls = Date.class;
System.out.println(cls);
}
}
"class java.util.Date"
Раньше объект класса Class был получен после создания экземпляра объекта Date, но теперь экземпляра объекта нет. Такой подход используется в Hibernate, Mybatis, Spring и т. д.
3. Вызовите метод, предоставляемый классом Class, — создайте экземпляр объекта Class (в java.lang.Objectpublic static
Class> forName(String className) выдает ClassNotFoundException):
public class ReflectTest5 {
public static void main(String[] args) throws ClassNotFoundException {
Class<?> cls = Class.forName("java.util.Date");
System.out.println(cls);
}
}
"class java.util.Date"
На этом этапе вы можете импортировать конкретный класс без использования оператора импорта, а имя класса описывается в виде строки.
экземпляр объекта отражения
Когда вы получаете класс, вы должны напрямую использовать ключевое слово new для создания экземпляра объекта, что является нашей обычной практикой. Если вы получаете объект класса Class, вы можете использовать отражение для создания экземпляра операции объекта:
public T newInstance() throws InstantiationException,IllegalAccessException
пример:Создание объектов с использованием отражения
/*正常实例化*/
class Book{
public Book(){
System.out.println("*** Book的构造方法 ***");
}
@Override
public String toString() {
return "--- 这是Book方法 ---";
}
}
public class ReflectTest6 {
public static void main(String[] args) throws ClassNotFoundException {
Book b = new Book();
System.out.println(b);
}
}
"*** Book的构造方法 ***
--- 这是Book方法 ---"
package com.jkx.lzh.test;
/*反射实例化*/
class Book{
public Book(){
System.out.println("*** Book的构造方法 ***");
}
@Override
public String toString() {
return "--- 这是Book方法 ---";
}
}
public class ReflectTest7 {
public static void main(String[] args) throws InstantiationException,
IllegalAccessException, ClassNotFoundException{
Class<?> cls = Class.forName("com.jkx.lzh.test.Book");
Object obj = cls.newInstance();
}
}
"*** Book的构造方法 ***"
class Book{
public Book(){
System.out.println("*** Book的构造方法 ***");
}
@Override
public String toString() {
return "--- 这是Book方法 ---";
}
}
public class ReflectTest8 {
public static void main(String[] args) throws InstantiationException,
IllegalAccessException, ClassNotFoundException{
Class<?> cls = Class.forName("com.jkx.lzh.test.Book");
Object obj = cls.newInstance();
Book b = (Book) obj;
System.out.println(b);
}
}
"*** Book的构造方法 ***
--- 这是Book方法 ---"
При отражении операция инстанцирования объекта в будущем завершается не только опорой на одно ключевое слово new, но и отражением, но это не означает, что new полностью заменено.
Поэтому кто-то скажет: «Следующие две строки кода эквивалентны: Book b = new Book(); Это не сложнее, не так ли?».
Class<?> cls = Class.forName("com.jkx.lzh.test.Book");
Object obj = cls.newInstance();
PS: В любом развитии новое является самым большим виновником сцепления. Все сцепление происходит из нового.
Пример:Соблюдайте заводской шаблон -->
interface Fruit{
void eat();
}
class Apple implements Fruit{
@Override
public void eat() {
System.out.println("* 吃苹果 *");
}
}
class Factory{
public static Fruit getInstance(String className){
if("apple" == className){
return new Apple();
}
return null;
}
}
public class TestFactory {
public static void main(String[] args) {
Fruit f = Factory.getInstance("apple");
f.eat();
}
}
"* 吃苹果 *"
Код такой же, как и выше, но в настоящее время, если мы хотим добавить подкласс интерфейса Fruit «оранжевый», это означает, что мы должны изменить методы фабричного шаблона.
class Orange implements Fruit{
@Override
public void eat() {
System.out.println("* 吃橘子 *");
}
}
class Factory{
public static Fruit getInstance(String className){
if("apple" == className){
return new Apple();
}else if("orange" == className){
return new Orange();
}
return null;
}
}
Видно, что каждый раз, когда добавляется подкласс интерфейса Fruit, фабричный класс должен модифицироваться, а что, если подкласс необходимо добавить в любое время?
Поскольку фабричные классы теперь создаются непосредственно с помощью ключевого слова new, ключевой момент всех проблем — это new. Чтобы решить эту проблему, мы можем полагаться только на отражение.
Способ изменения фабричного шаблона следующий:
package com.jkx.lzh.test;
interface Fruit{
void eat();
}
class Apple implements Fruit{
@Override
public void eat() {
System.out.println("* 吃苹果 *");
}
}
class Orange implements Fruit{
@Override
public void eat() {
System.out.println("* 吃橘子 *");
}
}
class Factory{
public static Fruit getInstance(String className){
Fruit f = null;
try {
f = (Fruit) Class.forName(className).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return f;
}
}
public class TestFactory {
public static void main(String[] args) {
Fruit f = Factory.getInstance("com.jkx.lzh.test.Apple");
f.eat();
}
}
На данный момент программа действительно достигла цели разделения, и она очень масштабируема.
Вызов конструктора с использованием отражения
В коде, написанном ранее, мы вызывали конструктор без аргументов в классе по умолчанию. Однако также возможно, что класс не предоставляет конструктор без аргументов.
пример:Наблюдайте за проблемами с текущей программой
Book.java
package com.jkx.lzh.po;
public class Book {
private String title;
private double price;
public Book(String title,double price){
this.title = title;
this.price = price;
}
public String toString(String title,double price) {
return "书名:"+ this.title + "价格: " + this.price ;
}
}
ReflectTest9.java
package com.jkx.lzh.test;
public class ReflectTest9 {
public static void main(String[] args) throws InstantiationException,
IllegalAccessException, ClassNotFoundException{
Class<?> cls = Class.forName("com.jkx.lzh.po.Book");
Object obj = cls.newInstance();
System.out.println(obj);
}
}
Вышеприведенный код выдает следующее исключение, так как Book не имеет конструктора без параметров (
Exception in thread "main" java.lang.InstantiationException: com.jkx.lzh.po.Book
at java.lang.Class.newInstance(Class.java:427)
at com.jkx.lzh.test.ReflectTest1.main(ReflectTest1.java:7)
Caused by: java.lang.NoSuchMethodException: com.jkx.lzh.po.Book.<init>()
at java.lang.Class.getConstructor0(Class.java:3082)
at java.lang.Class.newInstance(Class.java:412)
... 1 more
Итак, как решить эту проблему?
Такой конструктор предоставляется в классе Class для получения структуры:
- Получить все конструкторы: public Constructor>[] getConstructors() throws SecurityException;
- Получите конструктор, указывающий порядок параметров: public Constructor
(Class<?> ... ParameterTypes) throws NoSuchMethodException,SecurityException;
Оба вышеуказанных метода возвращают объекты класса "java.lang.reflect.Constructor". В этом классе мы предоставляем метод для создания объекта путем явной передачи содержимого параметризованного конструктора:
public T newInstance(Object... initargs) throws InstantiationException,IllegalAccessException,IllegalAccessException,IllegalArgumentException,InvocationTargetException;
пример:Явный вызов конструктора в классе
ReflectTest9.java изменен следующим образом:
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class ReflectTest9 {
public static void main(String[] args) throws InstantiationException,
IllegalAccessException, ClassNotFoundException, NoSuchMethodException,
SecurityException, IllegalArgumentException, InvocationTargetException{
Class<?> cls = Class.forName("com.jkx.lzh.po.Book");
//public Book(String title,double price);Constructor传的是有参构造的参数类型
Constructor<?> con = cls.getConstructor(String.class,double.class);
Object obj = con.newInstance("JAVA 反射,",82.22);
System.out.println(obj);
}
}
"书名:JAVA 反射价格: 82.22"
Вышеизложенное дает мне предупреждение: независимо от того, сколько конструкторов предусмотрено при разработке нашего простого класса Java, мы должны по крайней мере сохранить конструктор без аргументов.
отражение вызова метода
Обычные методы в классе можно вызывать только после того, как класс создал экземпляр объекта. И есть три метода создания экземпляров (новый, conle, отражение)
пример:определить класс
public class Book {
private String title;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
Этот класс имеет конструктор без параметров, поэтому при создании экземпляра объекта вы можете напрямую использовать метод newInstance, предоставляемый классом.
В классе Class предусмотрены следующие операции для получения Method() в классе:
1. Получить все методы класса: public Method[] getMethods() throws SecurityException;
public Method getMethod(String name,Class<?> ... ParameterTypes) throws NoSuchMethodException,SecurityException;
Вышеуказанные две операции возвращают объект класса "java.lang.reflect.Method". В этом классе мы фокусируемся на одном методе: вызов метода -->
public Object invoke(Object obj,Object args) throws IllegalAccessException,IllegalArgumentException,InvocationTargetException;
пример:отражение вызова метода
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectTest10 {
public static void main(String[] args) throws InstantiationException,
IllegalAccessException, ClassNotFoundException, NoSuchMethodException,
SecurityException, IllegalArgumentException, InvocationTargetException{
String fileName = "title";//要操作的成员
Class<?> cls = Class.forName("com.jkx.lzh.po.Book");
Object obj = cls.newInstance();//必须实例化对象
Method setMet = cls.getMethod("set"+ inticap(fileName) , String.class);
Method getMet = cls.getMethod("get"+ inticap(fileName));
setMet.invoke(obj, "JAVA 反射");
System.out.println(getMet.invoke(obj));
}
public static String inticap(String str){
return str.substring(0,1).toUpperCase() + str.substring(1);
}
}
"JAVA 反射"
На данный момент мы вообще не можем видеть тип операции, а это значит, что указанный вызов метода любого класса может быть реализован с помощью отражения.
участник вызова отражения
- Атрибутам в классе должно быть выделено место в памяти после создания экземпляра объекта этого класса. Метод получения членов предоставляется в классе Class:
1. Получить всех членов: public Filed[] getDeclaredFileds() выбрасывает SecurityException;
2. Получить указанный элемент: public Filed getDeclaredFiled(String name) выбрасывает NoSuchMethodException, SecurityException;
- Возвращаемый тип — это класс «java.lang.reflect.Filed», и в этом классе есть два важных метода:
1. Получите содержимое свойства: public Object getObject(Object obj) throws IllegalAccessException,IllegalArgumentException
2. Установите содержимое свойства: public void Object getObject (Object obj, Object values) throws IllegalAccessException, IllegalArgumentException
- Ниже класса "java.lang.reflect.AccessibleObject" (измененный после JDK1.8):
1. Исполняемый файл наследует конструктор, метод;
2. Подано: в этом классе есть метод: public void setAccessible(логический флаг) выдает исключение SecurityException, устанавливает, следует ли деинкапсулировать
пример:Теперь доступны следующие классы
package com.jkx.lzh.po;
public class Book {
private String title;
}
Этот класс определяет частное свойство, согласно нашему исходному подходу оно не должно использоваться внешним миром.
пример: ноРазвертка с помощью setAccessibleготовые к использованию
import java.lang.reflect.Field;
public class ReflectTest11 {
public static void main(String[] args) throws NoSuchFieldException,
SecurityException, ClassNotFoundException,
InstantiationException, IllegalAccessException {
Class<?> cls = Class.forName("com.jkx.lzh.po.Book");
Object obj = cls.newInstance();//必须实例化对象
Field titleField = cls.getDeclaredField("title");
titleField.setAccessible(true);
titleField.set(obj, "JAVA 反射");//相当于:Book对象.title = "JAVA 反射";
System.out.println(titleField.get(obj));//相当于:Book对象.title;
}
}
"JAVA 反射"
Конструкторы и обычные методы тоже можно отменить, но мы это делаем редко, и для доступа к атрибутам рекомендуется использовать settr и gettr