java基础动态代理和反射(一)-- 动态代理,反射,动态语言,静态语言

动态代理

  • 代理:本来应该自己做的事情,却请来了别人来做,被请的人就是代理对象。
  • 动态代理:在程序运行过程中产生的这个对象。动态代理其实就是通过反射来生成一个代理。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;//用户操作接口
interface UserDao {public abstract void add();public abstract void delect();public abstract void update();public abstract void find();
}
interface StudentDao {public abstract void login();public abstract void regist();
}//接口实现类
class UserDaoImpl implements UserDao {@Overridepublic void add() {System.err.println("add");}@Overridepublic void delect() {System.err.println("delect");}@Overridepublic void update() {System.err.println("update");}@Overridepublic void find() {System.err.println("find");}
}
class StudentDaoImpl implements StudentDao {@Overridepublic void login() {System.err.println("login");}@Overridepublic void regist() {System.err.println("regist");}
}//实现InvocationHandler接口
class MyInvocationHandler implements InvocationHandler {private Object target;// 膜标对象public MyInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("权限检验");Thread.sleep(1000);Object result = method.invoke(target, args);Thread.sleep(1000);System.out.println("日志记录");System.out.println("------------------");return result;}}//代理对象
public class inputStreamYH {public static void main(String[] args) throws InterruptedException {UserDao ud = new UserDaoImpl();System.out.println("---------1---------");ud.add();ud.delect();ud.update();ud.find();Thread.sleep(1000);System.out.println("--------2----------");MyInvocationHandler myInvocationHandler = new MyInvocationHandler(ud);UserDao u = (UserDao) Proxy.newProxyInstance(ud.getClass().getClassLoader(),ud.getClass().getInterfaces(),myInvocationHandler);u.add();u.delect();u.update();u.find();System.out.println("---------3---------");StudentDao s = new StudentDaoImpl();MyInvocationHandler my = new MyInvocationHandler(s);StudentDao u2 = (StudentDao) Proxy.newProxyInstance(s.getClass().getClassLoader(),s.getClass().getInterfaces(),my);u2.login();u2.regist();}
}

动态语言vs 静态语言

1、动态语言

    是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运行时代码可以根据某些条件改变自身结构。主要动态语言:Object-CC#JavaScriptPHPPythonErlang

2、静态语言

  •     与动态语言相对应的,运行时结构不可变的语言就是静态语言。如JavaCC++
  •     Java不是动态语言,但Java可以称之为准动态语言”。即Java有一定的动态性,我们可以利用反射机制、字节码操作获得类似动态语言的特性。
  •     Java的动态性让编程的时候更加灵活!

反射Reflection技术

反射 (Reflection) 是 Java 的特征之一,它允许运行中的 Java 程序获取自身的信息,并且可以操作类或对象的内部属性。其实就是动态加载一个指定的类,并获取该类中的所有的内容。而且将字节码文件封装成对象,并将字节码文件中的内容都封装成对象,这样便于操作这些成员。简单说:反射技术可以对一个类进行解剖。

简而言之,通过反射,我们可以在运行时获得程序或程序集中每一个类型的成员和成员的信息。程序中一般的对象的类型都是在编译期就确定下来的,而 Java 反射机制可以动态地创建对象并调用其属性,这样的对象的类型在编译期是未知的。所以我们可以通过反射机制直接创建对象,即使这个对象的类型在编译期是未知的。

反射的核心是 JVM 在运行时才动态加载类或调用方法/访问属性,它不需要事先(写代码的时候或编译期)知道运行对象是谁。

Java反射是Java被视为动态(或准动态)语言的一个关键性质。这个机制允许程序在运行时透过Reflection APIs取得任何一个已知名称的class的内部信息,包括其modifiers(诸如public, static 等)、superclass(例如Object)、实现之interfaces(例如Cloneable),也包括fields和methods的所有信息,并可于运行时改变fields内容或唤起methods。

Java反射机制容许程序在运行时加载、探知、使用编译期间完全未知的classes。换言之,Java可以加载一个运行时才得知名称的class,获得其完整结构。加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射

    Class类是 Java 反射的核心类,可以获取类的属性方法等内容。

Java 反射主要提供以下功能:

  • 在运行时判断任意一个对象所属的类;
  • 在运行时构造任意一个类的对象;
  • 在运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用private方法);
  • 在运行时调用任意一个对象的方法
  • 重点:是运行时而不是编译时

反射的好处:

  1. 可扩展性 :应用程序可以利用全限定名创建可扩展对象的实例,来使用来自外部的用户自定义类。
  2. 类浏览器和可视化开发环境 :一个类浏览器需要可以枚举类的成员。可视化开发环境(如 IDE)可以从利用反射中可用的类型信息中受益,以帮助程序员编写正确的代码。
  3. 调试器和测试工具 : 调试器需要能够检查一个类里的私有成员。测试工具可以利用反射来自动地调用类里定义的可被发现的 API 定义,以确保一组测试中有较高的代码覆盖率。

反射的缺点

尽管反射非常强大,但也不能滥用。如果一个功能可以不用反射完成,那么最好就不用。在我们使用反射技术时,下面几条内容应该牢记于心。

  • 性能开销 :反射涉及了动态类型的解析,所以 JVM 无法对这些代码进行优化。因此,反射操作的效率要比那些非反射操作低得多。我们应该避免在经常被执行的代码或对性能要求很高的程序中使用反射。
  • 安全限制 :使用反射技术要求程序必须在一个没有安全限制的环境中运行。如果一个程序必须在有安全限制的环境中运行,如 Applet,那么这就是个问题了。
  • 内部暴露 :由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方法),所以使用反射可能会导致意料之外的副作用,这可能导致代码功能失调并破坏可移植性。反射代码破坏了抽象性,因此当平台发生改变的时候,代码的行为就有可能也随着变化。

反射的主要用途

很多人都认为反射在实际的 Java 开发应用中并不广泛,其实不然。当我们在使用 IDE(如 Eclipse,IDEA)时,当我们输入一个对象或类并想调用它的属性或方法时,一按点号,编译器就会自动列出它的属性或方法,这里就会用到反射。

反射最重要的用途就是开发各种通用框架。很多框架(比如 Spring)都是配置化的(比如通过 XML 文件配置 Bean),为了保证框架的通用性,它们可能需要根据配置文件加载不同的对象或类,调用不同的方法,这个时候就必须用到反射,运行时动态加载需要加载的对象。

举一个例子,在运用 Struts 2 框架的开发中我们一般会在 struts.xml 里去配置 Action,比如:

<action name="login" class="org.ScZyhSoft.test.action.SimpleLoginAction"method="execute"><result>/shop/shop-index.jsp</result><result name="error">login.jsp</result>
</action>

配置文件与 Action 建立了一种映射关系,当 View 层发出请求时,请求会被 StrutsPrepareAndExecuteFilter 拦截,然后 StrutsPrepareAndExecuteFilter 会去动态地创建 Action 实例。比如我们请求 login.action,那么 StrutsPrepareAndExecuteFilter就会去解析struts.xml文件,检索action中name为login的Action,并根据class属性创建SimpleLoginAction实例,并用invoke方法来调用execute方法,这个过程离不开反射。

对与框架开发人员来说,反射虽小但作用非常大,它是各种容器实现的核心。而对于一般的开发者来说,不深入框架开发则用反射用的就会少一点,不过了解一下框架的底层机制有助于丰富自己的编程思想,也是很有益的。

反射的基本步骤:

1、获得Class对象,就是获取到指定的名称的字节码文件对象。

2、实例化对象,获得类的属性、方法或构造函数。

3、访问属性、调用方法、调用构造函数创建对象。

反射机制提供的功能

  • 在运行时判断任意一个对象所属的类
  • 在运行时构造任意一个类的对象
  • 在运行时判断任意一个类所具有的成员变量和方法
  • 在运行时获取泛型信息
  • 在运行时调用任意一个对象的成员变量和方法
  • 在运行时处理注解
  • 生成动态代理

反射相关的主要API

    java.lang.Class:代表一个类

接口

功能

Member

该接口可以获取有关类成员(域或者方法)后者构造函数的信息。

AccessibleObject

该类是域(field)对象、方法(method)对象、构造函数(constructor)对象的基础类。它提供了将反射的对象标记为在使用时取消默认 Java 语言访问控制检查的能力。

Array

该类提供动态地生成和访问JAVA数组的方法。

Constructor

提供一个类的构造函数的信息以及访问类的构造函数的接口。

Field

提供一个类的 Field 的信息以及访问类的域的接口。代表类的成员变量

Method

提供一个类的 Method 的信息以及访问类的方法的接口。

Modifier

提供了 static 方法和常量,对类和成员访问修饰符进行解码。

Proxy

提供动态地生成代理类和类实例的静态方法。

反射的基本运用

反射可以用于判断任意对象所属的类,获得 Class 对象,构造任意一个对象以及调用一个对象。这里我们介绍一下基本反射功能的使用和实现(反射相关的类一般都在 java.lang.relfect 包里)。

反射Class 类获得 Class 对象

    在Object类中定义了以下的方法,此方法将被所有子类继承:

    public final Class getClass()

    以上的方法返回值的类型是一个Class类,此类是Java反射的源头,实际上所谓反射从程序的运行结果来看也很好理解,即:可以通过对象反射求出类的名称。Class类是Java 反射机制的入口,封装了一个类或接口的运行时信息,通过调用Class类的方法可以获取这些信息。

  1. Class本身也是一个类
  2. Class 对象只能由系统建立对象
  3. 一个加载的类在JVM 中只会有一个Class实例
  4. 一个Class对象对应的是一个加载到JVM中的一个.class文件
  5. 每个类的实例都会记得自己是由哪个Class 实例所生成
  6. 通过Class可以完整地得到一个类中的所有被加载的结构
  7. Class类是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象

Class类的常用方法

 // 手动输入类名
Scanner s = new Scanner(System.in);
System.out.println("获取类");
String className = s.next();
Class<?> clazz = Class.forName(className);

获取Class字节码文件对象,有4种方式:

1 获取Class对象 Object ——> getClass()
//对象都有了还要反射干什么?
Student stu = new Student();
Class<? extends Student> stuClass1 = stu.getClass();
System.out.println(stuClass1.getName());2 任何数据类型(包括基本数据类型)都有一个静态的class属性。
//需要导入类的包,依赖太强,不导包就抛编译错误
Class<Student> stuClass2 = Student.class;
System.out.println(stuClass2 == stuClass1);3 通过Class类的静态方法:forName(String className),
//一个字符串可以传入也可写在配置文件中等多种方法
Class<?> stuClass3;
try {stuClass3 = Class.forName("com.niu.Student");// student的路径System.out.println(stuClass3);
} catch (ClassNotFoundException e) {e.printStackTrace();
} 4 其他方式(不做要求)
ClassLoader cl = this.getClass().getClassLoader();
Class clazz4 = cl.loadClass(“类的全类名”);	

哪些类型可以有Class字节码文件对象?

(1)class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类

(2)interface:接口

(3)[]:数组

(4)enum:枚举

(5)annotation:注解@interface

(6)primitivetype:基本数据类型

(7)void

Class c1= Object.class;
System.out.println("c1 :" + c1);        // c1 :class java.lang.Object
Class c2= Comparable.class;
System.out.println("c2 :" + c2);// c2 :interface java.lang.Comparable
Class c3= String[].class;
System.out.println("c3 :" + c3); // c3 :class [Ljava.lang.String;
Class c4= int[][].class;
System.out.println("c4 :" + c4);// c4 :class [[I
Class c5= ElementType.class;
System.out.println("c5 :" + c5);// c5 :class java.lang.annotation.ElementType
Class c6= Override.class;
System.out.println("c6 :" + c6);// c6 :interface java.lang.Override
Class c7= int.class;
System.out.println("c7 :" + c7); // c7 :int
Class c8= void.class;
System.out.println("c8 :" + c8);// c8 :void
Class c9= Class.class;
System.out.println("c9 :" + c9);// c9 :class java.lang.Class
int[] a= new int[10];
int[] b= new int[100];
Class c10= a.getClass();
System.out.println("c10 :" + c10);// c10 :class [I
Class c11= b.getClass();
System.out.println("c11 :" + c11);// c11 :class [I
// 只要元素类型与维度一样,就是同一个Class
System.out.println(c10== c11); //true// 获取Class类实例,如果泛型不确定可以使用?号代替
// 通过Class类静态forName(“类包名.类名”)获取Class类实例,建议使用这种形式
Class<?> c111 = Class.forName("Text");
System.out.println("c111:" + c111);// c111:class Text
// 通过对象.getClass获取Class类实例
Class<?> c21 = new Text1().getClass();
System.out.println("c21:" + c21);// c21:class Text1
// 通过使用类名.class获取Class类实例。
Class<?> c31 = Text1.class;
System.out.println("c31:" + c31);// c31:class Text1
// 如果已创建了引用类型的对象,则可以通过调用对象中的 getClass( )方法获取Class类实例。
String name = new String("111");
Class<?>  c41 = name.getClass();
System.out.println("c41:" + c41);//  c41:class java.lang.String
// 通过调用某个类的Class实例的getSuperClass()方法可以获取该类超类的Class实例。
Class<?>  c51 = name.getClass().getSuperclass();
System.out.println("c51:" + c51);// c51:class java.lang.Object
// 如果是基本数据类型的包装类,则可以通过包装类.TYPE获取Class类实例
Class<?>  c61 = Boolean.TYPE;
System.out.println("c61:" + c61);// c61:Boolean

判断是否为某个类的实例

一般地,我们用 instanceof 关键字来判断是否为某个类的实例。同时我们也可以借助反射中 Class 对象的 isInstance() 方法来判断是否为某个类的实例,它是一个 native 方法:

public native boolean isInstance(Object obj);

class.inInstance(obj)

这个对象能不能被转化为这个类

1.一个对象是本身类的一个对象

2.一个对象能被转化为本身类所继承类(父类的父类等)和实现的接口(接口的父接口)强转

3.所有对象都能被Object的强转

4.凡是null有关的都是false   class.inInstance(null)

类名.class和对象.getClass()几乎没有区别,因为一个类被类加载器加载后,就是唯一的一个类。

obj.instanceof(class)

也就是说这个对象是不是这种类型,

1.一个对象是本身类的一个对象

2.一个对象是本身类父类(父类的父类)和接口(接口的接口)的一个对象

3.所有对象都是Object

4.凡是null有关的都是false  null.instanceof(class)

package cn.itcast.bean;class A {
}class B extends A {
}public class Test {public static void main(String[] args) {B b = new B();A a = new A();A ba = new B();System.out.println("1------------");System.out.println(b instanceof B);//trueSystem.out.println(b instanceof A);//trueSystem.out.println(b instanceof Object);//trueSystem.out.println(null instanceof Object);//trueSystem.out.println("2------------");System.out.println(b.getClass().isInstance(b));//trueSystem.out.println(b.getClass().isInstance(a));//falseSystem.out.println("3------------");System.out.println(a.getClass().isInstance(ba));//trueSystem.out.println(b.getClass().isInstance(ba));//trueSystem.out.println(b.getClass().isInstance(null));//falseSystem.out.println("4------------");System.out.println(A.class.isInstance(a));//trueSystem.out.println(A.class.isInstance(b));//trueSystem.out.println(A.class.isInstance(ba));//trueSystem.out.println("5------------");System.out.println(B.class.isInstance(a));//falseSystem.out.println(B.class.isInstance(b));//trueSystem.out.println(B.class.isInstance(ba));//trueSystem.out.println("6------------");System.out.println(Object.class.isInstance(b));//trueSystem.out.println(Object.class.isInstance(null));//false}
}

反射创建指定类的对象:

    获取了字节码文件对象后,最终都需要创建指定类的对象:创建对象的两种方式(其实就是对象在进行实例化时的初始化方式):

1,调用空参数的构造函数:

使用了Class类中的newInstance()方法。

Class<?> c = String.class;
Object str = c.newInstance();// 如何生成获取到字节码文件对象的实例对象。类加载
Class clazz = Class.forName("cn.itcast.bean.Person");
clazz = Person.class;// 根据对象获得类型
Person obj3 = (Person)clazz.newInstance();
System.out.println("年龄"+obj3.getAge()+"姓名"+obj3.getName());
 

2,调用带参数的构造函数:

先要获取指定参数列表的构造函数对象,然后通过该构造函数的对象的newInstance(实际参数) 进行对象的初始化。

//获取String所对应的Class对象
Class<?> c = String.class;
//获取String类带一个String参数的构造器
Constructor constructor = c.getConstructor(String.class);
//根据构造器创建实例
Object obj = constructor.newInstance("23333");
System.out.println(obj);

综上所述,第二种方式,必须要先明确具体的构造函数的参数类型,不便于扩展。所以一般情况下,被反射的类,内部通常都会提供一个公有的空参数的构造函数。

// 如何生成获取到字节码文件对象的实例对象。类加载
Class clazz = Class.forName("cn.itcast.bean.Person");
//既然类中没有空参数的构造函数,那么只有获取指定参数的构造函数,用该函数来进行实例化。
//获取一个带参数的构造器。
Constructor constructor = clazz.getConstructor(String.class,int.class);
//想要对对象进行初始化,使用构造器的方法newInstance();
Person obj = (Person) constructor.newInstance("zhagnsan",30);
System.out.println("年龄"+obj.getAge()+"姓名"+obj.getName());// 直接获得指定的类型
clazz = Person.class;
Constructor constructor1 = clazz.getConstructor(String.class,int.class);
Person obj1 = (Person) constructor1.newInstance("zhagnsan1",3);
System.out.println("年龄"+obj1.getAge()+"姓名"+obj1.getName());

//该实例化对象的方法调用就是指定类中的空参数构造函数,给创建对象进行初始化。当指定类中没有空参数构造函数时,该如何创建该类对象呢?请看method_2();

public static void method_2() throws Exception {Class clazz = Class.forName("cn.itcast.bean.Person");//既然类中没有空参数的构造函数,那么只有获取指定参数的构造函数,用该函数来进行实例化。//获取一个带参数的构造器。Constructor constructor = clazz.getConstructor(String.class,int.class);//想要对对象进行初始化,使用构造器的方法newInstance();Person obj = (Person) constructor.newInstance("zhagnsan",30);System.out.println("年龄"+obj.getAge()+"姓名"+obj.getName());//获取所有构造器。Constructor[] constructors = clazz.getConstructors();//只包含公共的for(Constructor con : constructors) {System.out.println("只包含公共的"+con);//只包含公共的public cn.itcast.bean.Person(java.lang.String,int)//只包含公共的public cn.itcast.bean.Person()}constructors = clazz.getDeclaredConstructors();//包含私有的for(Constructor con : constructors) {System.out.println("包含私有的"+con);//包含私有的private cn.itcast.bean.Person(java.lang.String)//包含私有的public cn.itcast.bean.Person(java.lang.String,int)//包含私有的public cn.itcast.bean.Person()}
}package cn.itcast.bean;public class Person {String name;int age;public Person () {}public Person (String name, int age) {this.name = name;this.age = age;}private Person (String name) {this.age = age;}@Overridepublic String toString () {return "Person{" +"name='" + name + '\'' +", age=" + age +'}';}public String getName () {return name;}public void setName (String name) {this.name = name;}public void setAge (int age) {this.age = age;}public int getAge () {return age;}
}

反射指定类中的方法:  

//获取类中所有的方法。public static void method_1() throws Exception {Class clazz = Class.forName("cn.itcast.bean.Person");Method[] methods = clazz.getMethods();//获取的是该类中的公有方法和父类中的公有方法。methods = clazz.getDeclaredMethods();//获取本类中的方法,包含私有方法。for(Method method : methods) {System.out.println(method);}}//获取指定方法;public static void method_2() throws Exception {Class clazz = Class.forName("cn.itcast.bean.Person");//获取指定名称的方法。Method method = clazz.getMethod("show", int.class,String.class);//想要运行指定方法,当然是方法对象最清楚,为了让方法运行,调用方法对象的invoke方法即可,但是方法运行必须要明确所属的对象和具体的实际参数。Object obj = clazz.newInstance();method.invoke(obj, 39,"hehehe");//执行一个方法}//想要运行私有方法。public static void method_3() throws Exception {Class clazz = Class.forName("cn.itcast.bean.Person");//想要获取私有方法。必须用getDeclearMethod();Method method = clazz.getDeclaredMethod("method", null);// 私有方法不能直接访问,因为权限不够。非要访问,可以通过暴力的方式。method.setAccessible(true);//一般很少用,因为私有就是隐藏起来,所以尽量不要访问。}//反射静态方法。public static void method_4() throws Exception {Class clazz = Class.forName("cn.itcast.bean.Person");Method method = clazz.getMethod("function",null);method.invoke(null,null);}

获取类的成员变量(字段)信息(Fields

可以通过反射机制得到某个类的某个属性,然后改变对应于这个类的某个实例的该属性值。JAVA 的Class类提供了几个方法获取类的属性。

getField(String name)

  • public Field getField(String name)

返回一个 Field 对象,它反映此 Class 对象所表示的类或接口或父类的指定公共成员字段,name参数指定了属性的名称,返回该Class对象表示类或接口中与指定属性名(含继承的)相同的public 属性对应的Field对象。

getFields()

  • public Field[] getFields()

返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段,返回一个该Class对象表示类或接口中所有public属性(含继承的)对应的Field对象数组。

getDeclaredField(String name)

  • public Field getDeclaredField(String name)

返回一个 Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段,name参数指定了属性的名称,返回一个与该Class对象表示类或接口内定义的所有属性名(不含继承的)相匹配的属性相对应的Field对象。

getDeclaredFields()

  • public Field[] getDeclaredFields()

返回 Field 对象的一个数组,获取类中所有的属性(public、protected、default、private),但不包括继承的属性,返回 Field 对象的一个数组。返回一个该Class对象表示类或接口内定义的所有属性(不含继承的)对应的Field对象数组,。

getFields和getDeclaredFields区别:

getFields返回的是申明为public的属性,包括父类中定义,

getDeclaredFields返回的是指定类定义的所有定义的属性,不包括父类的。

Field 类对象常用方法

获取变量的类型:

Field.getType():返回这个变量的类型

Field.getGenericType():如果当前属性有签名属性类型就返回,否则就返回 Field.getType()

isEnumConstant() : 判断这个属性是否是枚举类

获取成员变量的修饰符

Field.getModifiers()  以整数形式返回由此 Field 对象表示的字段的 Java 语言修饰符

获取和修改成员变量的值

getName() : 获取属性的名字

get(Object obj) 返回指定对象obj上此 Field 表示的字段的值

set(Object obj, Object value) 将指定对象变量上此 Field 对象表示的字段设置为指定的新值

主要是这几个方法,在此不再赘述:

import java.lang.reflect.Field;class A {public static String a1 ="as";private String a2;protected String a3;String a4;
}class B extends A {public static int b1 =99;private String b2;protected String b3;String b4;
}public class Test {public static void main(String[] args) throws IllegalAccessException, NoSuchFieldException {//第一步:获取操作类Class clazz = B.class;//第二步:获取此类的所有的公共(public)的字段,返回 Field 对象的一个数组System.out.println("getField   "+clazz.getField("a1").get(clazz));Field[] fields = clazz.getFields();for (Field field : fields) {//第三步:获取字段的名称System.out.println("getFields  field.getName()  "+field.getName());System.out.println("getFields  getType()  "+field.getType());System.out.println("getFields  field.isEnumConstant()  "+field.isEnumConstant());System.out.println("getFields  field.getGenericType()  "+field.getGenericType());System.out.println("getFields  field.getModifiers()  "+field.getModifiers());
//            field.set(clazz,21);System.out.println("getFields  field.get()  "+field.get(clazz));}//第二步:获取此类的所有的公共(public)的字段,返回 Field 对象的一个数组System.out.println("getDeclaredField   "+clazz.getDeclaredField("b1").get(clazz));Field[] fields1 = clazz.getDeclaredFields();for (Field field : fields1) {//第三步:获取字段的名称String fieldValue = field.getName();System.out.println("getDeclaredFields    "+fieldValue);}}
}
getField   as
getFields  field.getName()  b1
getFields  getType()  int
getFields  field.isEnumConstant()  false
getFields  field.getGenericType()  int
getFields  field.getModifiers()  9
getFields  field.get()  99
getFields  field.getName()  a1
getFields  getType()  class java.lang.String
getFields  field.isEnumConstant()  false
getFields  field.getGenericType()  class java.lang.String
getFields  field.getModifiers()  9
getFields  field.get()  as
getDeclaredField   99
getDeclaredFields    b1
getDeclaredFields    b2
getDeclaredFields    b3
getDeclaredFields    b4

常见错误

set(Object obj, Object value) 时,新value和原value的类型不一致导致,如下:无法转换类型导致的 java.lang.IllegalArgumentException(注意:反射获取或者修改一个变量的值时,编译器不会进行自动装/拆箱,所以int 和Integer需手动修改)

set(Object obj, Object value) 时,修改 final类型的变量导致的 IllegalAccessException。由于 Field 继承自 AccessibleObject , 我们可以使用 AccessibleObject.setAccessible() 方法告诉安全机制,这个变量可以访问即可解决,如field.setAccessible(true)。

getField(String name) 或getFields() 获取非 public 的变量,编译器会报 java.lang.NoSuchFieldException 错

获取方法

获取某个Class对象的方法集合,主要有以下几个方法:

Method[] getMethods()返回一个该Class对象表示类或接口中所有public方法(含继承的)对应的Method对象数组。

Method[] array = clazz.getMethods();

for (Method a : array) {

     System.out.println(a.toString());

}

Thread.sleep(1000);

Method getMethod(String methodName, Class<?>... parameterTypes):返回与该Class对象表示类或接口中方法名和方法形参类型相匹配的public方法(含继承的)的Method对象。其中第一个参数为方法名称,后面的参数为方法的参数对应Class的对象

Method array2 = clazz.getMethod("Text_1", String.class);

Object obj1 = clazz.newInstance();

array2.invoke(obj1, "hehehe");

Method[] getDeclaredMethods():返回一个该Class对象表示类或接口内声明定义的所有访问权限的方法,包括公共、保护、默认(包)访问和私有方法,(不含继承的)对应的Method对象数组

Method array4 = clazz.getDeclaredMethod("Text_1", String.class);

Object obj1 = clazz.newInstance();

array2.invoke(obj1, "hehehe");

Method getDeclaredMethod(String methodName,Class<?>... parameterTypes) 返回与该Class对象表示类或接口中方法名和方法形参类型相匹配的所有访问权限的方法,包括公共、保护、默认(包)访问和私有方法,(不含继承的)对应的Method对象。其中第一个参数为方法名称,后面的参数为方法的参数对应Class的对象

Method[] array1 = clazz.getDeclaredMethods();

for (Method a : array1) {       

    System.out.println(a.toString());

}

只是这样描述的话可能难以理解,我们用例子来理解这三个方法:

例子
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;public static void test() throws Exception {Class<?> c = methodClass.class;Object object = c.newInstance();Method[] methods = c.getMethods();//getMethods()方法获取的所有方法System.out.println("getMethods获取的方法:开始");for(Method m:methods){System.out.println(m);}Thread.sleep(1000);System.out.println("getMethods获取的方法:结束");Method[] declaredMethods = c.getDeclaredMethods();//getDeclaredMethods()方法获取的所有方法System.out.println("getDeclaredMethods获取的方法::开始");for(Method m:declaredMethods) {System.out.println(m);}Thread.sleep(1000);System.out.println("getDeclaredMethods获取的方法:结束");//获取methodClass类的add方法Method method = c.getMethod("add", int.class, int.class);}
}
class methodClass extends  methodCl{public final int fuck = 3;public int add(int a,int b) throws InterruptedException {return a+b;}public int sub(int a,int b) {return a+b;}
}
class methodCl {public final int fuck1 = 3;public int add1(int a,int b) {return a+b;}private int sub2(int a,int b) {return a+b;}
}
程序运行的结果如下:
getMethods获取的方法:开始
public int cn.itcast.bean.methodClass.add(int,int)
public int cn.itcast.bean.methodClass.sub(int,int)
public int cn.itcast.bean.methodCl.add1(int,int)
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
getMethods获取的方法:结束
getDeclaredMethods获取的方法::开始
public int cn.itcast.bean.methodClass.add(int,int)
public int cn.itcast.bean.methodClass.sub(int,int)
getDeclaredMethods获取的方法:结束

可以看到,通过 getMethods() 获取的方法可以获取到父类的方法,比如 java.lang.Object 下定义的各个方法。

通过调用Class对象的getMethods、getMethod、getDeclaredMethods和getDeclaredMethod方法可以获取到Class对象表示类中某些或某个具体方法所对应Method对象,

Method对象有一下常用方法:

Class<?> getDeclaringClass()返回声明Method对象表示方法的类或接口的 Class 对象。

int getModifiers()以整数形式返回此Method对象所表示方法的修饰符。应该使用Modifier类对修饰符进行解码。

Class<?> getReturnType()返回Method对象所表示的方法的返回值类型所对应的Class对象。

String getName()返回方法名。

Class<?>[] getParameterTypes()返回由Method对象代表方法的形参类型对应Class对象组成的数组。如果方法没有参数,则数组长度为 0。

Class<?>[] getExceptionTypes()返回由Method对象表示方法抛出异常类型对应Class对象组成的数组。如果此方法没有在其 throws子句中声明异常,则返回长度为 0 的数组。

例子
Class<?> cl = methodClass.class;
//获取methodClass类的add方法
Method c = cl.getMethod("add", int.class, int.class);
// Class<?> getDeclaringClass():返回声明Method对象表示方法的类或接口的 Class 对象。
Class<?> cla = c.getDeclaringClass();
System.out.println("方法的类或接口: " + cla.getName());
// int getModifiers():以整数形式返回此Method对象所表示方法的修饰符。应该使用Modifier类对修饰符进行解码。
int a = c.getModifiers();
System.out.println("方法的修饰符解码: " + a);
System.out.println("方法的修饰符: " + Modifier.toString(a));
// Class<?> getReturnType():返回Method对象所表示的方法的返回值类型所对应的Class对象。
Class<?> aa = c.getReturnType();
System.out.println("返回值类型: " + aa.toString());
// String getName():返回方法名。
System.out.println("方法名: " + c.getName());
// Class<?>[] getParameterTypes():返回由Method对象代表方法的形参类型对应Class对象组成的数组。
// 如果方法没有参数,则数组长度为 0。
Class<?>[] par = c.getParameterTypes();
for (Class<?> a1 : par) {System.out.println("参数: " + a1.getName());
}
// Class<?>[] getExceptionTypes():返回由Method对象表示方法抛出异常类型对应Class对象组成的数组。
// 如果此方法没有在其 throws子句中声明异常,则返回长度为 0 的数组。
Class<?>[] ex = c.getExceptionTypes();
for (Class<?> a2 : ex) {System.out.println("异常: " + a2.getName());
}
返回结果
方法的类或接口: cn.itcast.bean.methodClass
方法的修饰符解码: 1
方法的修饰符: public
返回值类型: int
方法名: add
参数: int
参数: int
异常: java.lang.InterruptedException

返回Class对象表示类构造方法

获取类构造器的用法与上述获取方法的用法类似。主要是通过Class类的getConstructor方法得到Constructor类的一个实例,而Constructor类有一个newInstance方法可以创建一个对象实例:

public T newInstance(Object ... initargs)

此方法可以根据传入的参数来调用对应的Constructor创建对象实例。

Constructor[] getConstrutors():

Constructor[] getConstrutors()返回该Class对象表示类包含的所有public构造方法(不含继承)所对应Constructor对象数组。如果没有构造方法,则返回一个默认的无参构造方法:

       

Class<Text2> clazz = Text2.class;Constructor[] con = clazz.getConstructors();for (Constructor a : con) {System.out.println(a.toString());}

Constructor getConstrutor(Class<?>... parameterTypes):

Constructor getConstrutor(Class<?>... parameterTypes)返回与该Class对象表示类中参数列表相匹配的public构造函数(不含继承)对应的Constructor对象。

Constructor con1 = clazz.getConstructor(int.class);
System.out.println(con1);
public Text2(int)

Constructor<?>[] getDeclaredConstructors()

Constructor<?>[] getDeclaredConstructors()返回一个该Class对象表示类中声明的所有构造方法(不区分访问权限)对应的Constructor对象数组。

        Constructor<?>[] con2 = clazz.getDeclaredConstructors();

        for (Constructor a : con2) {

            System.out.println(a.toString());

        }

Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes):

Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)返回与该Class对象表示类中定义的形参类型相匹配的构造方法(不区分访问权限)的Constructor对象。

Constructor<Text2> con3 = clazz.getConstructor(int.class);

System.out.println(con3);

public class Test {public static void main(String[] args) throws Exception{//根据指定的文件路径返回一个Class对象Class cls = User.class;Constructor[] con = cls.getConstructors();for (Constructor a : con) {System.out.println(a.toString());}Thread.sleep(100);System.out.println("--------------------");Constructor[] con1 = cls.getDeclaredConstructors();for (Constructor a : con1) {System.out.println(a.toString());}Thread.sleep(100);System.out.println("--------------------");//指定获取带有两个参数的构造器,要指定参类型、参数的位置、参数的个数Constructor cons = cls.getConstructor(int.class,String.class);// 通过这个构造器对象创建实体类对象User user = (User)cons.newInstance(15,"“嘻嘻”");System.out.println(user.getAge());Thread.sleep(100);System.out.println("--------------------");Constructor<User> cons1 = cls.getDeclaredConstructor(int.class);// 通过这个构造器对象创建实体类对象User user1 = cons1.newInstance(1599);System.out.println(user1.getAge());}
}
class User {private int age;private String name;public User(int age, String name) {super();this.age = age;this.name = name;}private User(int age) {super();this.age = age;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public User() {super();}
}

通过调用Class对象的getConstrutors()、getConstrutor、getDeclaredConstructors和getDeclaredConstructor方法可以获取到Class对象表示类中某些或某个具体构造方法所对应Constructor对象,Constructor对象有一下常用方法:

Class<T> getDeclaringClass()返回声明Constructor对象对应构造方法的类的Class对象。

int getModifiers()以整数形式返回Constructor对象表示的构造函数的修饰符。

String getName() 以字符串形式返回Constructor对象所表示得构造方法的名称。

Class<?>[] getParameterTypes()返回由Constructor对象所表示的构造方法的形参类型对应Class对象组成的数组此 。如果构造方法没有参数,则数组长度为0。

Constructor<?> con = clazz.getConstructor(int.class, int.class, int.class);

System.out.println("构造方法的类名称: " + con.getName());

// 以字符串形式返回Constructor对象所表示得构造方法的名称。

Class<?> dec = con.getDeclaringClass();

System.out.println("构造方法的类或接口: " + dec.getName());

// 返回声明Constructor对象对应构造方法的类的Class对象。构造方法的类或接口: Text2

int cll = con.getModifiers();

System.out.println("构造方法的整数形式返回字段的修饰符: " + cll);

// 以整数形式返回Constructor对象表示的字段的修饰符。构造方法的整数形式返回字段的修饰符: 1

System.err.println("构造方法的tostring返回字符串的修饰符: " +Modifier.toString(cll));

// 返回Constructor对象表示的字段的修饰符。构造方法的tostring返回字符串的修饰符: public

Class<?>[] par = con.getParameterTypes();

for (Class<?> p : par) {

    System.out.println("构造方法的参数: " + p);

System.out.println("构造方法的参数名称字符串: " + p.getName());

    // 返回由Constructor对象所表示的构造方法的形参类型对应Class对象组成的数组此 。

// 如果构造方法没有参数,则数组长度为0。

构造方法的参数: int

构造方法的参数名称字符串: int

}

通过反射调用方法

当我们从类中获取了一个方法后,我们就可以用 invoke()方法来调用这个方法。

通过调用Method对象的invoke方法可以实现对方法的调用:

Object invoke(Object obj, Object... args)调用Method对象指代的方法并返回Object类型结果。obj表示该方法所在类实例,如果方法时静态的则obj可以指定为null; args表示传入该方法的参数,如果方法没有参数,则args数组长度可以为 0 或 null 。

通过调用Field对象的如下方法可以获取或设置属性的值:

Object get(Object obj)返回Field表示字段的Object类型的值。obj为该属性所在类创建的对象,如果该属性是静态的,则可设置为null。

void set(Object obj, Object value)为Field对象表示属性设置新值。obj为该属性所在类创建的对象,如果该属性为静态的则课设置为null;value为该属性新值。

invoke 方法的原型为:

public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException,

           InvocationTargetException

下面是一个实例:

Class<?> klass = methodClass.class;
//创建methodClass的实例
Object obj = klass.newInstance();
//获取methodClass类的add方法
Method method = klass.getMethod("add",int.class,int.class);
//调用method对应的方法 => add(1,4)
Object result = method.invoke(obj,1,4);
System.out.println(result);

关于 invoke 方法的详解,后面我会专门写一篇文章来深入解析 invoke 的过程。

利用反射创建数组

数组在Java里是比较特殊的一种类型,它可以赋值给一个Object Reference。下面我们看一看利用反射创建数组的例子:

public static void testArray() throws ClassNotFoundException {Class<?> cls = Class.forName("java.lang.String");Object array = Array.newInstance(cls,25);//往数组里添加内容Array.set(array,0,"hello");Array.set(array,1,"Java");Array.set(array,2,"fuck");Array.set(array,3,"Scala");Array.set(array,4,"Clojure");//获取某一项的内容System.out.println(Array.get(array,3));
}

其中的Array类为java.lang.reflect.Array类。我们通过Array.newInstance()创建数组对象,它的原型是:

public static Object newInstance(Class<?> componentType, int length)
throws NegativeArraySizeException {return newArray(componentType, length);
}

而 newArray 方法是一个 native 方法,它在 HotSpot JVM 里的具体实现我们后边再研究,这里先把源码贴出来:

private static native Object newArray(Class<?> componentType, int length)
throws NegativeArraySizeException;

源码目录:openjdk\hotspot\src\share\vm\runtime\reflection.cpp

arrayOop Reflection::reflect_new_array(oop element_mirror, jint length, TRAPS) {if (element_mirror == NULL) {THROW_0(vmSymbols::java_lang_NullPointerException());}if (length < 0) {THROW_0(vmSymbols::java_lang_NegativeArraySizeException());}if (java_lang_Class::is_primitive(element_mirror)) {Klass* tak = basic_type_mirror_to_arrayklass(element_mirror, CHECK_NULL);return TypeArrayKlass::cast(tak)->allocate(length, THREAD);} else {Klass* k = java_lang_Class::as_Klass(element_mirror);if (k->oop_is_array() && ArrayKlass::cast(k)->dimension() >= MAX_DIM) {THROW_0(vmSymbols::java_lang_IllegalArgumentException());}return oopFactory::new_objArray(k, length, THREAD);}
}

另外,Array 类的 set 和 get 方法都为 native 方法,在 HotSpot JVM 里分别对应 Reflection::array_set 和 Reflection::array_get 方法,这里就不详细解析了。

反射的一些注意事项

由于反射会额外消耗一定的系统资源,因此如果不需要动态地创建一个对象,那么就不需要用反射。

另外,反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题。

Java反射机制六问

JVM加载class文件的原理机制?

    JVM中类的装载是由ClassLoader和它的子类来实现的,Java ClassLoader 是一个重要的Java运行时系统组件。它负责在运行时查找和装入类文件的类。

利用反射和重载完成以下功能

1)创建Student类,类中有属性name和age并封装属性

2)重载Student的构造函数,一个是无参构造并,另一个是带两个参数的有参构造,要求在构造函数打印提示信息

3)创建带main函数的NewInstanceTest类,利用Class类得到Student对象

4)通过上述获取的Class对象分别调用Student有参函数和无参函数

利用反射的知识完成下面的操作

    创建Mytxt类,创建myCreate()方法完成创建文件D:\myhello.txt文件的功能。创建带main方法的NewInstanceTest类,通过Class类获取Mytxt对象,调用myCreat()

利用Annotation和反射知识完成操作

    1)自定义一个有效的Annotation注释名为MyAnnotation,其中有属性myname创建Student类并重写toString(),toString()要求使用三个基本的Annotation和自定义的MyAnnotation注释

    2)创建TestGetAnno类,打印出Student类的toString方法的所有注释

利用通过反射修改私有成员变量

  1. 定义PrivateTest类,有私有name属性,并且属性值为hellokitty,只提供name的getName的公有方法
  2. 创建带有main方法ReflectTest的类,利用Class类得到私有的name属性
  3. 修改私有的name属性值,并调用getName()的方法打印name属性值

利用反射和File完成以下功能

  1. 利用Class类的forName方法得到File类
  2. 在控制台打印File类的所有构造器
  3. 通过newInstance的方法创建File对象,并创建D:\mynew.txt文件

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/293909.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

苹果推出Swift开发教程 无需编码知识小白也能学

简介 苹果推出Swift开发教程&#xff0c;教授开发者如何使用 Swift、SwiftUI 和 Xcode 开发 iOS 应用。从基本的界面设计到复杂的数据建模和空间计算。据苹果公司称&#xff0c;网站上提供的教程 "适合所有人"&#xff0c;即使是那些没有任何编码经验的人。教程提供…

让工作自动化起来!无所不能的Python

让工作自动化起来&#xff01;无所不能的Python 让工作自动化起来&#xff01;无所不能的Python编辑推荐内容简介作者简介前言为什么要写这本书读者对象如何阅读本书 博主 默语带您 Go to New World. ✍ 个人主页—— 默语 的博客&#x1f466;&#x1f3fb; 《java 面试题大全…

Java中常见的锁策略

目录 乐观锁 vs 悲观锁 悲观锁: 乐观锁&#xff1a; 重量级锁 vs 轻量级锁 ⾃旋锁&#xff08;Spin Lock&#xff09; 公平锁 vs 非公平锁 可重⼊锁 vs 不可重入锁 读写锁 乐观锁 vs 悲观锁 悲观锁: 总是假设最坏的情况&#xff0c;每次去拿数据的时候都认为别…

【DETR系列目标检测算法代码精讲】01 DETR算法03 Dataloader代码精讲

与一般的Dataloader的区别在于我们对图像进行了随机裁剪&#xff0c;需要进行额外的操作才能将其打包到dataloader里面 这一段的代码如下&#xff1a; if args.distributed:sampler_train DistributedSampler(dataset_train)sampler_val DistributedSampler(dataset_val, shu…

C语言动态内存讲解+通讯录2.0

文章目录 前文malloc和freecallocrealloc枚举常量的简单说明及使用 通讯录2.0动态开辟通讯录,满了就扩容保存数据和载入数据 通讯录2.0演示推荐好用的软件 前文 本文主要介绍动态开辟的几个函数,以及改进之前的通讯录。 我们局部变量等是在栈区上开辟空间的,而我们动态开辟的空…

非wpf应用程序项目【类库、用户控件库】中使用HandyControl

文章速览 前言参考文章实现方法1、添加HandyControl包&#xff1b;2、添加资源字典3、修改资源字典内容 坚持记录实属不易&#xff0c;希望友善多金的码友能够随手点一个赞。 共同创建氛围更加良好的开发者社区&#xff01; 谢谢~ 前言 wpf应用程序中&#xff0c;在入口项目中…

Linux 给网卡配置ip

ip addr | grep eth9 ifconfig eth9 10.0.0.2 netmask 255.255.255.0 up

linux安装git

一、下载git 注意&#xff1a;不要下载最新版本的git&#xff0c;否则可能安装会失败&#xff0c;缺失很多依赖文件&#xff0c;解决起来费时费力&#xff0c;还可能不成功 尽量下载前几年的&#xff0c;甚至10年前的都可以 下载地址&#xff1a;https://mirrors.edge.kerne…

Codigger用户篇:安全、稳定、高效的运行环境(一)

在当今数字化时代&#xff0c;个人数据的安全与隐私保护显得尤为重要。为了满足用户对数据信息的安全需求&#xff0c;我们推出Codigger分布式操作系统&#xff0c;它提供了一个运行私有应用程序的平台&#xff0c;旨在为用户提供一个安全、稳定、高效的私人应用运行环境。Codi…

3.26号arm

1. SPI相关理论 1.1 概述 spi是一种同步全双工串行总线&#xff0c;全称串行外围设备接口 通常SPI通过4个引脚与外部器件相连&#xff1a; MISO&#xff1a;主设备输入/从设备输出引脚。该引脚在从模式下发送数据&#xff0c;在主模式下接收数据。 MOSI&#xff1a;主设备输…

Go-Gin-Example 第八部分 优化配置接口+图片上传功能

文章目录 前情提要本节目标 优化配置结构讲解落实修改配置文件优化配置读取及设置初始化顺序第一步 验证 抽离file 实现上传图片接口图片名加密封装image的处理逻辑编写上传图片的业务逻辑增加图片上传的路由 验证实现前端访问 http.FileServerr.StaticFS修改文章接口新增、更新…

以太网/USB 数据采集卡 24位16通道 labview 256K同步采样

XM7016以太网SUB数据采集卡 XM7016是一款以太网/USB高速数据采集卡&#xff0c;具有16通道真差分输入&#xff0c;24位分辨率&#xff0c;单通道最高采样率256ksps. 16通道同步共计4.096Msps、精密前置增益放大、集成IEPE/ICP硬件支持的特点。本产品采用了多个高精度24位ADC单元…

学习JavaEE的日子 Day32 线程池

Day32 线程池 1.引入 一个线程完成一项任务所需时间为&#xff1a; 创建线程时间 - Time1线程中执行任务的时间 - Time2销毁线程时间 - Time3 2.为什么需要线程池(重要) 线程池技术正是关注如何缩短或调整Time1和Time3的时间&#xff0c;从而提高程序的性能。项目中可以把Time…

【 MyBatis 】| 关于多表联查返回 List 集合只查到一条的 BUG

目录 一. &#x1f981; 写在前面二. &#x1f981; 探索过程2.1 开端 —— 开始写 bug2.2 发展 —— bug 完成2.3 高潮 —— bug探究2.4 结局 —— 效果展示 三. &#x1f981; 写在最后 一. &#x1f981; 写在前面 今天又是 BUG 气满满的一天&#xff0c;一个 xxxMapper.xm…

跑spark的yarn模式时RM连不上的情况

在linux控制台跑spark on yarn一个测试案例&#xff0c;日志中总显示RM连yarn服务的时候是&#xff1a;0.0.0.0:8032 具体情况如下图&#xff1a; 我问题出现的原因&#xff0c;总结如下&#xff1a; 1.防火墙没关闭&#xff0c;关闭 2.spark-env.sh这个文件的YARN_CONF_DIR…

MyBatis基础使用

MyBatis首页https://mybatis.net.cn/ MyBatis细节注意&#xff0c;让你更加熟悉MyBatishttps://blog.csdn.net/m0_61160520/article/details/137173558?spm1001.2014.3001.5501 1.项目目录 2.数据库 CREATE DATABASE mybatis-example;USE mybatis-example;CREATE TABLE t_e…

Linux文件与进程交互的窥探者lsof

lsof 是一个 Linux 和 UNIX 系统中的实用工具,用于列出系统中打开文件的所有信息。这个名字代表 “List Open Files”,但它也可以显示进程相关的其他信息,如: 打开的文件描述符列表 打开网络连接的列表 被进程使用的信号和内核对象等 在Linux系统中,有一个经典的概念: …

vue3+threejs新手从零开发卡牌游戏(二十):添加卡牌被破坏进入墓地逻辑

在game目录下新建graveyard文件夹存放墓地相关代码&#xff1a; game/graveyard/p1.vue&#xff0c;这里主要设置了墓地group的位置&#xff1a; <template><div></div> </template><script setup lang"ts"> import { reactive, ref,…

【刷题】 二分查找入门

送给大家一句话: 总有一天&#xff0c;你会站在最亮的地方&#xff0c;活成自己曾经渴望的模样—— 苑子文 & 苑子豪《我们都一样 年轻又彷徨》 二分查找入门 1 前言2 Leetcode 704. 二分查找2.1 题目描述2.2 算法思路 3 Leetcode 34. 在排序数组中查找元素的第一个和最后…

求组合背包II(acwing)

题目描述&#xff1a; 给定n组循问&#xff0c;每组询问给定两个整数a&#xff0c;b&#xff0c;请你输出Ca^b mod (1e9 7)的值&#xff0c;。 输入格式&#xff1a; 第一行包含整数n。 接下来2行&#xff0c;每行包含一组a和b。 输出格式&#xff1a; …