16.JavaSE-反射

示例引出反射

请看下面的问题
  1. 根据配置文件 re.properties 指定信息,创建Cat对象并调用方法hi。配置文件的信息如下:
ini 复制代码
   classfullpath=com.reflect_.Cat
   method=hi

使用现有技术,你能做的吗?

  1. 这样的需求在学习框架时特别多,即通过外部文件配置,在不修改源码情况下,来控制程序,也符合设计模式的ocp原则(开闭原则:不修改源码,扩容功能)
使用反射解决上面问题
java 复制代码
public class Start_ {
    public static void main(String[] args) throws Exception {
        //1.使用Properties 类,可以读写配置文件
        Properties properties = new Properties();
        properties.load(new FileInputStream( "src\\re.properties"));
        String classfullpath = properties.get("classfullpath").toString();//"com.reflect_.Cat"
        String methodName = properties.get( "method" ).toString();// "hi"
        System.out.println("classfullpath=" + classfullpath);
        System.out.println( "methodName=" + methodName);
        //2.创建对象,传统的方法,行不通=>反射机制
        // new classfullpath();
        //3.使用反射机制解决
        // (1) 获取class类型的对象(有一个类,类名叫Class。通过forName方法可以获取classfullpath路径类的加载的对象)
        Class<?> cls = Class.forName(classfullpath);
        // (2) 获取com.reflect_.Cat 的对象实例
        //     cls.getDeclaredConstructor()获取无参构造器
        //     cls.getDeclaredConstructors()获取所有无参构造器,返回是一个数组
        Object o = cls.getDeclaredConstructor().newInstance();
        System.out.println(o.getClass());
        //(3)获取方法对象
        Method method = cls.getMethod(methodName);
        //(4) 通过反射来调用hi方法
        method.invoke(o);
    }
}

反射机制

反射机制允许程序在执行期借助于ReflectionAPI取得任何类的内部信息(比如成员变量,构造器,成员方法等等),并能操作对象的属性及方法。反射在设计模式和框架底层都会用到。

加载完类之后,在堆中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含了类的完整结构信息。通过这个对象得到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,形象的称之为:反射。

反射原理

这张图清晰地展示了Java类从编写到运行的完整生命周期以及反射机制的底层内存模型,整体分为编译、类加载和运行时三个阶段。

  • 首先在编译阶段 ,Cat.java源码经过javac编译后,生成包含字段、方法描述符以及常量池(如"招财猫")的二进制字节码文件Cat.class。
  • 接着进入类加载阶段 ,类加载器(ClassLoader)通过loadClass方法按需(懒加载)读取字节码,将真正的类元数据(如字节码指令、方法结构)存入方法区(元空间),同时在堆内存中生成一个代表该类的Class对象作为访问入口,该对象内部持有指向方法区元数据的指针。
  • 最后在Runtime运行时阶段 ,当我们实例化Cat对象时,会在堆中分配内存,该实例的对象头指针会指向堆中的Class对象(即通过getClass()获取的镜像),而实例中的name字段则指向堆内存里的字符串常量池中的"招财猫"。
反射的作用

在运行时判断任意一个对象所属的类

在运行时构造任意一个类的对象

在运行时得到任意一个类所具有的成员变量和方法

在运行时调用任意一个对象的成员变量和方法

生成动态代理

反射相关的主要类
  1. java.lang.Class:代表一个类,Class对象表示某个类加载后在堆中的对象
  2. java.lang.reflect.Method:代表类的方法, 只能取public修饰的成员
  3. java.lang.reflect.Field:代表类的成员变量, 只能取public修饰的成员
  4. java.lang.reflect.Constructor:代表类的构造方法
java 复制代码
public class ReflecTion_ {
    public static void main(String[] args) throws Exception {
        //1.使用Properties 类,可以读写配置文件
        Properties properties = new Properties();
        properties.load(new FileInputStream( "src\\re.properties"));
        String classfullpath = properties.get("classfullpath").toString();//"com.reflect_.Cat"
        String methodName = properties.get( "method" ).toString();// "hi
        
        Class cls = Class.forName(classfullpath);
        // 通过无参构造器创建对象
        Object o = cls.getConstructor().newInstance();
        System.out.println(o.getClass());
        // 方法对象
        Method method = cls.getMethod(methodName); 
        method.invoke(o);//执行方法
        // 获取对象
        Field name = cls.getField("name"); //注意只能取public修饰的成员
        System.out.println(name.get(o));//获取对象
        // 获取构造器对象
        Constructor csc = cls.getConstructor(); //无参构造器
        System.out.println(csc);
        // 有参构造器 String.class这个是String class对象
        Constructor csc1 = cls.getConstructor(String.class);
        System.out.println(csc1);
    }
}

优点和缺点

  1. 优点:可以动态的创建和使用对象(也是框架底层核心),使用灵活,没有反射机制,框架技术就失去底层支撑。

  2. 缺点:使用反射基本是解释执行,对执行速度有影响.

Class类&类对象

JVM 在运行时为每个加载的类在堆中生成唯一的 Class 对象,对象实例通过对象头中的指针记住自己的 Class。反射的核心,就是利用字符串(Class.forName())或 .class 语法获取这个 Class 对象,然后通过它动态地获取类的结构信息,并在必要时通过 setAccessible(true) 突破访问控制,动态地创建实例、调用方法或修改属性。

  • 运行时的类型标识(RTTI) : Java 是多态语言。当你调用 animal.sound() 时,JVM 怎么知道该调猫叫还是狗叫?靠的就是对象的"类型标识"。每个对象在内存里都有一张"身份证",记录自己属于哪个类。虚拟机利用运行时类型信息选择相应的方法执行。

  • Class 类不需要通过new创建对象,而是JVM创建对象的。你不能写 new Class(),因为 Class 的构造器是私有的。它是由 JVM 在加载类时自动在堆中生成的。

  • 无论是 new Cat() 还是反射 constructor.newInstance(),创建实例时,JVM 会先确保这个类已经被 ClassLoader 加载过了(如果没有,先加载),然后分配内存、调用构造器

  • 一个类在内存中只有一份 Class 对象,是因为 JVM 的双亲委派模型会缓存加载过的类。

  • 每个类的实例都会记得自己是由哪个Class对象所生成 ,因为JVM 会在对象头(Object Header)里存一个指针,指向堆中的这个 Class 对象。所以你可以通过 obj.getClass() 顺藤摸瓜找到它。

  • JDK 8 以后, Class 对象本身存放在堆中,真正的类元数据(字节码指令、字段描述、方法代码等)存放在方法区(元空间 Metaspace) 。堆里的 Class 对象内部有一个指针,指向方法区里的元数据。

  • 反射的核心就在这里。因为 Class 对象持有这些元数据,所以你可以调用 getDeclaredFields()、getDeclaredMethods() 等方法,像剥洋葱一样拿到类的所有细节。

类的实例之所以能进行反射操作(或者动态绑定),是因为对象头里有一个指针(Klass Pointer)指向堆中的 Class 对象;而这个 Class 对象内部又有一个指针指向方法区里的元数据。这样 JVM 就能顺着实例 -> Class 对象 -> 元数据这条链路,找到字段的偏移量和方法的字节码,从而操作属性或调用方法。

一个通俗的类比:

  • 实例对象(cat):你本人。
  • 对象头指针:你随身带的身份证。
  • Class 对象(堆中):派出所的户籍档案柜(只记录你属于哪个街道,具体信息在系统里)。
  • 方法区元数据:公安局的系统数据库(真正存着你的姓名、年龄、指纹)。

如果你只是想知道自己叫什么(直接访问 cat.name),你摸摸脑袋就回忆起来了,不需要去查档案。

但如果你要办业务,工作人员(反射)必须拿你的身份证(对象头),去档案柜(Class 对象),再查系统数据库(元数据),才能确认你的所有信息并办理业务。

Class类实际上是一个泛型类。例如,Employee.class的类型是Class<Employee>。没有说明这个问题的原因是:它将已经抽象的概念更加复杂化了。在大多数实际问题中,可以忽略类型参数,而使用原始的Class类。这样 employeeClass.newInstance() 出来的对象直接就是 Employee 类型,不用强转。

class 常用方法
java 复制代码
public class Class_ {
    public static void main(String[] args) throws Exception {
        //获取到Car类对应的 CLass对象
        Class<?> cls = Class.forName("com.reflect_.Cat");
        System.out.println(cls);//显示cls对象,是哪个类的Class对象
        System.out.println(cls.getClass());//输出cls运行类型java.lang.Class
        //得到包名
        System.out.println(cls.getPackage().getName());//包名
        //得到全类名
        System.out.println(cls.getName());
        //通过cls创建对象实例
        Cat cat = (Cat)cls.getConstructor().newInstance();
        System.out.println(cat);
        //只能获取本类及父类 public 字段
        Field name = cls.getField("name");
        System.out.println(name.get(cat));
        //获取本类及父类 public 方法,getMethods也一样
        // getDeclaredMethods本类所有方法,不含父类。
        Method method = cls.getMethod("hi"); 
        method.invoke(o);//执行方法
        //通过反射设置值
        name.set(cat, "大脸猫");
        System.out.println(name.get(cat));
        //获取所有的属性,只能获取本类及父类 public 字段
        // getDeclaredFields() 本类所有字段,不含父类。
        Field[] fields = cls.getFields();
        for (Field field : fields) {
            System.out.println(field.get(cat));
        }
    }
}
获取Class对象的方式
  1. 前提: 已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,forName默认会初始化类,可能抛出ClassNotFoundException

    实例: Class cls1 =Class.forName( "java.lang.Cat");

    应用场景: 多用于配着文件,读取类全路径,加载类,一般处于类编译后想要获取Class对象可以使用这种方式。

  2. 前提: 若已知具体的类,通过类的class 获取,该方式最为安全可靠,程序性能最高

    实例: Class cls2 = Cat.class;

    应用场景: 多用于参数传递,比如通过反射得到对应构造器对象。一般在类加载阶段,想要获取Class对象可以使用这种方式。

  3. 前提: 已知某个类的实例,调用该实例的getClass()方法获取Class对象

    实例: Class class=对象.getClass();

    应用场景: 通过创建好的对象,获取Class对象。一般处于运行阶段,想要获取Class对象可以使用这种方式。

  4. 其他方式:

    ClassLoader cl =对象.getClass().getClassLoader();

    loadClass只加载,不初始化

    Class clazz4 = cl.loadClass("类的全类名");

  5. 基本数据(int, char,boolean,float,double,byte,long,short)按如下方式得到Class类对象

    Class cls =基本数据类型.class

  6. 基本数据类型对应的包装类,可以通过.TYPE得到Class类对象

    Class cls =包装类.TYPE

JAVA 复制代码
public class GetClass_ {
    public static void main(String[] args) throws ClassNotFoundException {
        //1.通过反射获取类
        Class<?> cls1 = Class.forName("com.reflect_.Cat");
        System.out.println(cls1);
        //2.通过类名.class
        Class<Cat> cls2 = Cat.class;
        System.out.println(cls2);
        //3.通过对象.getClass()
        Cat cat = new Cat();
        Class cls3 = cat.getClass();
        System.out.println(cls3);
        //4.通过类加载器获取类的class对象
        //(1).先获取类的加载器 cat
        ClassLoader classLoader = cat.getClass().getClassLoader();
        //(2).通过类加载器获取Class对象
        Class<?> cls4 = classLoader.loadClass("com.reflect_.Cat");
        System.out.println(cls4);
    }
}
哪些类型有class类的对象
  1. 外部类,成员内部类,静态内部类,局部内部类,匿名内部类

  2. interface

  3. 数组

  4. enum

  5. annotation

  6. 基本数据类型

  7. void

java 复制代码
public class AllTypeClass {
    public static void main(String[] args) {
        Class<String> cls1 = String.class;//外部类
        System.out.println(cls1);	
        Class<Serializable> cls2 = Serializable.class;//接口
        System.out.println(cls2);
        Class<Integer[]> cls3 = Integer[].class;//数组
        System.out.println(cls3);
        Class<float[][]> cls4 = float[][].class;//二维数组
        System.out.println(cls4);
        Class<Deprecated> cls5 = Deprecated.class;//注解//枚举
        System.out.println(cls5);
        Class<Thread.State> cls6 = Thread.State.class;
        System.out.println(cls6);
        Class<Long> cls7 = long.class;//基本数据类型
        System.out.println(cls7);
        Class<Void> cls8 = void.class; //void数据类型
        System.out.println(cls8);
        Class<Class> cls9 = Class.class;
        System.out.println(cls9);
    }
}

类加载

反射机制是java实现动态语言的关键,也就是通过反射实现类动态加载。

  1. 静态加载:编译时就需要加载相关的类,如果没有则报错,依赖性太强。

    java 复制代码
    public static void main(string[] args){
    	Scanner scanner = new Scanner(System.in);
        System.out.println("请输入key");
    	String key = scanner.next();
        switch(key) {
    		case "1" :
        		Dog dog = new Dog();//静态加载
    			break;
    		case "2":
    			System.out.println( "ok " );
                break;
    		default:
    			System.out.println( "do nothing.." );
    	}
    }

    如上述代码中Dog类需要在编译的时候就需要加载进去,如果发现没有这个类就会报错。

  2. 动态/延时加载: 运行时加载需要的类,如果运行时不用该类,即使不存在该类,也不会报错, 降低了依赖性。

    java 复制代码
    public static void main(string[] args){
    	Scanner scanner = new Scanner(System.in);
        System.out.println("请输入key");
    	String key = scanner.next();
        switch(key) {
    		case "1" :
        		System.out.println( "1" );
    			break;
    		case "2":
                //使用反射动态加载,运行时只有执行到这段代码,才会加载Person类
                Class cls = Class.forName( "Person");//运行时加载Person类
                Object o = cls.newInstance();
                Method m = cls.getMethod( "hi" );m.invoke(o);
    			System.out.println( "ok " );
                break;
    		default:
    			System.out.println( "do nothing.." );
    	}
    }

类加载时机:

  1. 当创建对象时(new) 属于静态加载
  2. 当子类被加载时,父类也加载 属于静态加载
  3. 调用类中的静态成员时 属于静态加载
  4. 通过反射 属于动态加载
类加载流程图

在加载(Loading)阶段:

JVM 将字节码从不同数据源(class文件、jar包、网络)转化为二进制字节流加载到内存。具体分三步:

  1. 通过全限定名获取二进制字节流。
  2. 将字节流代表的静态存储结构转化为方法区的运行时数据结构(元数据)。
  3. 在堆内存 中生成一个代表该类的 java.lang.Class 对象,作为方法区数据的访问入口。

在连接(Linking)阶段进行的操作:

  • 验证: 确保Class文件字节流安全且符合规范(文件格式、元数据、字节码、符号引用验证)

  • 准备: 为静态变量分配内存并设置默认初始值 (0、null、false)。如果是编译期常量 (static final 且编译时值确定),则直接赋值。实例变量不在此阶段分配内存。

    java 复制代码
    class A {
    	//n1 是实例属性,不是静态变量,因此在准备阶段,是不会分配内存
        public int n1 = 10;
        //n2是静态变量,分配内存 n2是默认初始化0,而不是20
        public static int n2 = 20;
    	//n3是static final是常量,他和静态变量不一样,因为一旦赋值就不变,所以在准备阶段会直接赋值。
    	public static final int n3 = 50;
    }
  • 解析:将常量池内的符号引用替换为直接引用(内存地址)。

将类的二进制数据合并到 JVM 的运行时数据区中(即方法区和堆)

**初始化阶段:**执行类构造器 <clinit>() 方法,按源码顺序收集静态变量的真实赋值动作和静态代码块,真正赋予程序员设定的初始值。

获取类的结构信息

类的结构信息
java 复制代码
package reflect_;

import org.junit.Test;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class ReflectUtil_ {
    public static void main(String[] args) {
    }

    @Test
    public void api01() throws ClassNotFoundException {
        Class<?> cls = Class.forName("reflect_.Dog");
        //getName:获取全类名
        System.out.println(cls.getName());
        //getSimpleName:获取简单类名
        System.out.println(cls.getSimpleName());
        //getFields:获取所有public修饰的属性,包含本类以及父类的
        Field[] fields = cls.getFields();
        for (Field field : fields) {
            System.out.println("public修饰的属性"+field);
        }
        //getDeclaredFields:获取本类中所有属性.
        Field[] declaredFields = cls.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println("本类中所有属性"+declaredField);
        }
        //getMethods:获取所有public修饰的方法,包含本类以及父类的
        Method[] methods = cls.getMethods();
        for (Method method : methods) {
            System.out.println("public修饰的方法"+method);
        }
        //getDeclaredMethods:获取本类中所有方法
        Method[] declaredMethods = cls.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println("本类中所有方法"+declaredMethod);
        }
        //getConstructors:获取所有public修饰的构造器.只包含本类
        Constructor<?>[] constructors = cls.getConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println("public修饰的构造器,只包含本类的"+constructor);
        }
        //getDeclaredconstructors:获取本类中所有构造器.
        Constructor<?>[] declaredConstructors = cls.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            System.out.println("本类中所有构造器"+declaredConstructor);
        }
        //getPackage:以Package形式返回包信息.
        System.out.println(cls.getPackage());
        //getsuperClass:以Class形式返回父类信息
        System.out.println(cls.getSuperclass());
        //getInterfaces:以class[]形式返回接口信息
        Class<?>[] interfaces = cls.getInterfaces();
        for (Class<?> anInterface : interfaces) {
            System.out.println("接口信息"+anInterface);
        }
        //getAnnotations:以Annotation[]形式返回注解信息,注意只返回RUNTIME保留的注解,例如@Override 这种 SOURCE 注解拿不到
        Annotation[] annotations = cls.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println("注解信息"+annotation);
        }
    }
}

interface A1{}
interface A2{}

class Animal {
    public String hobby;
    public Animal() {}
    public Animal(String hobby) {
        this.hobby = hobby;
    }
    public void a1(){}
    public void a2(){}
}

@Deprecated
class Dog extends Animal implements A1,A2{
    public String name;
    protected int age;
    String job;
    private double salary;
    public Dog(){}
    
    public Dog(String name) {
        this.name = name;
    }
    public Dog(String name, int age) {
        this.name = name;
        this.age = age;
    }
    private Dog(int age) {
        this.age = age;
    }
    public void m1(){}
    protected void m2(){}
}
类的属性信息
  1. getModifiers: 以int形式返回修饰符,建议用 Modifier.toString(field.getModifiers()), 这里说明下:

    默认修饰符是 0,public是 1,private是 2,protected是 4, static是 8,final是 16

    组合使用例如:public static final = public(1) + static (8) + final(16) =25

    还有别的修饰符: synchronized=32、volatile=64、transient=128、native=256、interface=512、abstract=1024 等

  2. getType:以Class形式返回类型

  3. getName:返回属性名

java 复制代码
@Test
public void api02() throws ClassNotFoundException {
    Class<?> cls = Class.forName("reflect_.Dog");
    Field[] declaredFields = cls.getDeclaredFields();
    for (Field declaredField : declaredFields) {
        System.out.println("本类中所有属性:"+declaredField+"; 属性修饰符的值是:"+Modifier.toString(declaredField.getModifiers())
                           +"; 属性的类型是:"+declaredField.getType());
    }
}
类的方法信息
  1. getModifiers: 以int形式返回修饰符,建议用 Modifier.toString(field.getModifiers()), 和类的属性的一样。
  2. getReturnType:以Class形式获取返回类型
  3. getName:返回方法名
  4. getParameterTypes:以Class\[\]返回参数类型数组
JAVA 复制代码
@Test
public void api03() throws ClassNotFoundException {
    Class<?> cls = Class.forName("reflect_.Dog");
    //getDeclaredMethods:获取本类中所有方法
    Method[] declaredMethods = cls.getDeclaredMethods();
    for (Method declaredMethod : declaredMethods) {
        System.out.println("本类中所有方法"+declaredMethod+"; 方法修饰符的值是:"+Modifier.toString(declaredMethod.getModifiers())+
                           "; 方法的返回类型是:"+declaredMethod.getReturnType());
        System.out.println("该方法的参数个数是:"+declaredMethod.getParameterCount());
        Class<?>[] parameterTypes = declaredMethod.getParameterTypes();
        for (Class<?> parameterType : parameterTypes) {
            System.out.println("该方法的参数类型是"+parameterType);
        }
    }
}
类的构造器信息
  1. getModifiers:以int形式返回修饰符
  2. getName:返回构造器名(全类名)
  3. getParameterTypes:以Class\[\]返回参数类型数组

反射爆破

通过反射创建对象
  1. Class类相关方法

    newInstance: 调用类中的无参构造器,获取对应类的对象。

    getConstructor(Class...clazz):根据参数列表,获取对应的public构造器对象

    getDeclaredConstructor(Class...clazz):根据参数列表,获取对应的所有构造器对象

  2. Constructor类相关方法

    setAccessible:暴破, 使用反射可以访问private构造器/属性/方法。

    newlnstance(Object...obj): 调用构造器

java 复制代码
package reflect_;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class ReflectCreateInstance {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        //1 先获取Person类的class对象,通过无参构造器创建对象
        Class<?> cls = Class.forName("reflect_.Person");
        Object o = cls.getConstructor().newInstance();
        System.out.println(o);
        //2 通过public修饰的的有参构造器创建实例
        Constructor<?> cst1 = cls.getConstructor(String.class);
        //这里面传递的参数就和Person(String name)一样
        System.out.println(cst1.newInstance("小明"));
        //3 通过非public的无参构造器创建实例
        //3.1 得到构造器对象
        Constructor<?> dcst1 = cls.getDeclaredConstructor(String.class, int.class);
        Constructor<?> dcst2 = cls.getDeclaredConstructor(String.class, int.class, String.class, double.class);
        //3.2 非protected和无修饰的可以直接newInstance
        System.out.println(dcst1.newInstance("小花", 18));
        System.out.println(dcst2.newInstance("小李", 22, "业务员", 3000));
        //3.3 private修饰的需要爆破后才能newInstance
        Constructor<?> dcst3 = cls.getDeclaredConstructor(String.class, int.class, String.class);
        dcst3.setAccessible(true); //在 JDK 9+ 模块化下可能失败
        System.out.println(dcst3.newInstance("小龙", 20, "程序员"));
    }
}

class Person{
    public String name;
    protected int age;
    String job;
    private double salary;

    public Person() {}

    public Person(String name) {
        this.name = name;
    }

    protected Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    private Person(String name, int age, String job) {
        this.name = name;
        this.age = age;
        this.job = job;
    }

    Person(String name, int age, String job, double salary) {
        this.name = name;
        this.age = age;
        this.job = job;
        this.salary = salary;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", job='" + job + '\'' +
                ", salary=" + salary +
                '}';
    }
}
通过反射操作属性
  1. 根据属性名获取Field对象

    Field f = clazz对象.getDeclaredField(属性名);

  2. 暴破:f.setAccessible(true); //f 是Field

  3. 属性的操作

    f.set(o,值);

    f.get(o);

  4. 注意:如果是静态属性,则set和get中的参数o,也可以写成null, 因为静态程序不需要对象。

通过反射操作方法
  1. 根据方法名和参数列表获取Method方法对象:

    Method m =clazz.getDeclaredMethod(方法名,XX.class); 多个参数后边继续跟xx.class之间用逗号隔开

  2. 获取对象:Object o = clazz.newlnstance();

  3. 暴破:m.setAccessible(true);

  4. 访问:Object returnValue = m.invoke(o, 实参列表);

  5. 注意:如果是静态方法,则invoke的参数o,也可以写成null!

相关推荐
范什么特西1 小时前
加深动态代理的理解
java
天天被压力1 小时前
【Python量化实战宝典 #03】指数与板块行情接口全拆解:大盘·行业·概念一键取(Python 可跑)
java·人工智能·python
\光辉岁月/1 小时前
14.JavaSE-IOStream
java·io·stream
云和数据.ChenGuang1 小时前
Spring AI 对接 LangGraph:构建企业级智能体工作流实战
java·人工智能·spring boot·后端·spring
云和数据.ChenGuang1 小时前
Spring AI 入门实战:构建你的第一个 AI 应用
java·人工智能·深度学习·spring·机器学习·语言模型
下雨天没带伞1 小时前
Java Stream API实战,告别for循环
java
用户3721574261351 小时前
Java 高效管理 Excel 批注:添加、读取、修改和删除
java
C语言小火车1 小时前
C++ 为什么越来越依赖标准库?
java·开发语言·c++
西索斯coding2 小时前
在 中接入 教程:p配置、m填写、扩展思考参数一篇就够(2)
java·前端·javascript·ai