java35-克隆-浅克隆-深克隆-java参数值传递-RMI-代理模式-静态代理-动态代理JDK-invocationHandler-newProxyInstance

本文主要是介绍java35-克隆-浅克隆-深克隆-java参数值传递-RMI-代理模式-静态代理-动态代理JDK-invocationHandler-newProxyInstance,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

时间
4/25

文章目录

      • 克隆
        • 概述
        • 实现克隆的方式
          • 实现Cloneable接口,并重写object类中的clone方法,可以实现浅克隆,也可以实现深克隆
          • 实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的克隆
          • 利用BeanUtils,apache和spring都提供了bean工具,它是浅克隆
        • 浅克隆(Shallow Clone) 和 深克隆(Deep Clone)
          • 浅克隆(Shallow Clone)
          • 深克隆(Deep Clone)
        • 细节对比点
          • new操作符创建对象和clone方法复制对象的区别
          • 复制引用 和 复制对象 区别
          • 克隆两种实现方式的区别
      • java参数传递 = 值传递
      • RMI
      • Java中的动态代理
        • 代理模式
        • 静态代理
        • 动态代理
          • 基于接口的代理JDK
          • 基于继承的代理 CGlib

克隆

概述

克隆出现的原因:
在实际开发过程中,一个对象obj已经包含一些有用信息,这是我们需要将obj的信息复制到obj2中,使得obj和obj2对象具有两个完全不同的地址,修改一个对象的值,另一个对象不受影响。

实现克隆的方式
实现Cloneable接口,并重写object类中的clone方法,可以实现浅克隆,也可以实现深克隆
1 被克隆对象所属类必须实现Cloneable接口public class 类名 implements Cloneable{private 属性类 属性名;}
1-1 要想实现深克隆的话,类的成员属性 所在的类也需要实现Cloneable接口public class 属性类 implements Cloneable{}
2 所属类必须重写Object类的clone方法@Overridepublic Object clone() throws CloneNotSupportedException{类名 obj =(类名)super.clone(); // 浅克隆obj.属性名 = (属性类)属性名.clone(); //深克隆return obj;
}
实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的克隆
// 要序列化对象所属类 需要实现Serializable接口
public class 类名 implements Serializable{}
// 序列化
1 创建序列化流ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("文件路径"));
2 创建对象类名 obj = new 类名();
3 使用序列化流的writeObject方法将对象写入指定的ObjectOutputStream流中oos.writeObject(obj);
4 释放资源oos.close();// 反序列化
1 创建反序列化流ObjectInputStream ois = new ObjectInputStream(new FileInputStream("文件路径"));
2 使用反序列化流的readObject方法从ObjectInputStream流中读取一个对象Object obj = ois.readObject();
3 强制转化对象类型类型 obj1 = (类型)obj;
4 释放资源ois.close();
利用BeanUtils,apache和spring都提供了bean工具,它是浅克隆

扩展

  • BeanUtils 是工具类,用于简化JavaBean的封装,简化数据的封装。(xhj理解:和collections是一样的,同样是工具类)
  • apache 是 世界使用排名第一的Web服务器软件,具有跨平台和安全性的优势,是最流行的Web服务器端软件之一。
  • Spring框架是一个开放源代码的J2EE应用程序框架,针对bean的生命周期进行管理的轻量级容器。
    在Spring中,构成应用程序主干并由Spring IOC容器管理的对象称为bean。bean是一个由Spring IOC容器实例化、组装和管理的对象。
    实例的创建不再由调用者管理,而是由Spring容器创建,容器会负责控制程序之间的关系,而不是由程序代码直接控制。
浅克隆(Shallow Clone) 和 深克隆(Deep Clone)

两种克隆方式主要区别在于是否支持引用类型的成员变量的复制。
java中的数据类型分为:基本数据类型(数值型{整型、浮点型、字符型}) 和 引用数据类型(接口、类、数组)

浅克隆(Shallow Clone)

浅克隆中,对象的复制只是复制它本身和其中包含的值类型的成员变量,而引用类型的成员变量并没有复制。
实现方法
覆盖Object类中的Clone()方法实现。

1 被复制的类需要实现Cloneable接口public class 类名 implements Cloneable{}
2 覆盖clone()方法,方法调用super.clone()方法得到需要的复制对象,输入clone有选项可以选择,自动生成。 修改为public访问级别@Overridepublic Object clone() throws CloneNotSupportedException {return super.clone();}

实验分析:

1 被复制的类没有实现Cloneable接口,而直接使用  类名 对象2 = (类名)对象1.clone(); 会报错报clone()方法在java.lang.Object中是protected访问控制。
2 被复制的类实现Cloneable接口,使用 类名 对象2 = (类名)对象1.clone();  也会报错,需要抛出异常CloneNotSupportedException异常

案例:

// 320-test1
// 类
public class Student implements Cloneable{private String name;private int age;public Student() {}public Student(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}// 测试类
public class StudentDemo {public static void main(String[] args) throws CloneNotSupportedException {Student s1 = new Student("汪苏泷",35);Student s2 = (Student)s1.clone();System.out.println(s1.getName() + ", " + s1.getAge());System.out.println("--------------");System.out.println(s2.getName() + ", " + s2.getAge());System.out.println(s1 == s2);// false 说明对象在堆内存中的地址不同}
}
深克隆(Deep Clone)

深克隆中,无论原型对象的成员变量的类型是值类型还是引用数据类型,都会复制一份给克隆对象。
实现方法:
方法一:通过覆盖Object类中的clone()方法实现

1 被复制对象的类 需要实现Cloneable接口,并重写Object类中的clone()方法。public class Outer implements Cloneable{private Inner inner;@Overridepublic Object clone() throws CloneNotSupportedException{Outer obj = (Outer)super.clone(); //类对象使用clone()方法obj.inner = (Inner)inner.clone();//类对象属性的类 使用clone()方法return obj;}}
2 Inner类也要实现Cloneable接口,重写Object类中的clone()方法。public class Inner implements Cloneable{@Overridepublic Object clone() throws CloneNotSupportedException{Inner obj = (Inner)super.clone();}
* 输入clone有选项可以选择 修改为public访问级别

方法二:使用序列化(Serialization)实现,序列化流ObjectOutputStream、反序列化流ObjectInputStream

// 序列化
1 创建序列化流对象ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("文件路径"));
2 创建对象类名 obj1 = new 类名();
3 使用序列化方法writeObject(Object obj)oos.writeObject(obj1);
4 释放资源oos.close();// 反序列化
1 创建反序列化对象ObjectInputStream ois = new ObjectInputStream(new FileInputStream("文件路径"));
2 使用反序列化方法readObject()Object obj = ois.readObject();
3 强制类型转换 Object =》 类名类名 obj2 =(类名)obj;
4 释放资源ois.close();

方法一案例:

// 320-test2
// Student的属性 的类
public class Address implements Cloneable {private String address;public Address() {}public Address(String address) {this.address = address;}public String getAddress() {return address;}public void setAddress(String address) {this.address = address;}@Overridepublic Object clone() throws CloneNotSupportedException {return super.clone();}
}// Student 要克隆的对象所属的类
public class Student implements Cloneable{private String name;private int age;private Address addr;// 创建的是Address类的对象public Student() {}public Student(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public Address getAddr() {return addr;}public void setAddr(Address addr) {this.addr = addr;}@Overridepublic Object clone() throws CloneNotSupportedException {Student stu = (Student)super.clone();//浅克隆stu.addr = (Address)addr.clone();//深克隆return stu;}
}
// 测试类
public class StudentDemo {public static void main(String[] args) throws CloneNotSupportedException {Address addr = new Address("西安市");Student s1 = new Student("汪苏泷",35);s1.setAddr(addr);Student s2 = (Student)s1.clone();System.out.println(s1.getName() + ", " + s1.getAge()+", "+s1.getAddr().getAddress());System.out.println(s2.getName() + ", " + s2.getAge()+", " + s2.getAddr().getAddress());
//        System.out.println(s1 == s2);// false 说明对象在堆内存中的地址不同// 修改前输出:   汪苏泷, 35, 西安市//              汪苏泷, 35, 西安市// 两个对象的Addr变量都一样,为了验证原对象和新对象是两个不一样的,这里对s2对象的Addr变量修改值addr.setAddress("重庆市");System.out.println("修改之后");System.out.println("--------------");System.out.println(s1.getName() + ", " + s1.getAge()+", "+s1.getAddr().getAddress());System.out.println(s2.getName() + ", " + s2.getAge()+", " + s2.getAddr().getAddress());// 对s2对象的成员变量addr进行修改,s1对象的addr变量也发生了变化。//  =》 说明 没有实现深克隆,只完成了浅克隆,成员是值类型的复制。// 要想实现深克隆,需要给Student类中的addr变量所处在类Address设置为浅克隆实现。// Address实现cloneable、且重写clone方法// 修改前输出:汪苏泷, 35, 重庆市//           汪苏泷, 35, 重庆市// 修改后输出:汪苏泷, 35, 重庆市//           汪苏泷, 35, 西安市}
}
细节对比点
new操作符创建对象和clone方法复制对象的区别
具体操作newclone
分配内存根据new操作符后面的类型分配对应大小的内存空间分配与源对象相同大小的内存空间
填充对象的域调用构造方法,填充对象的各个域(对象的初始化)使用源对象中对应的各个域填充新对象的域
对象创建完毕构造方法返回创建完毕的对象,并将其引用地址发布到外部(栈内存)clone()方法返回对象,并将其引用发布到外部(栈内存)
复制引用 和 复制对象 区别

A 是 源对象,B 是 新对象
复制引用 = A 和 B 地址值 相同,说明是一个对象
复制对象 = A 和 B 地址值 不相同,说明是两个对象

克隆两种实现方式的区别
区别项实现Cloneable接口的方式序列化和反序列化方式
是否可以实现深度克隆可以,
若对象属性嵌套很多引用类型,则需要在类中写多个.clone()方法,使用不是很方便
可以,使用方便
是否支持编译时检测异常不支持,是在运行时抛出异常的支持,通过泛型限定,可以检查出克隆的对象是否支持序列化,在编译阶段完成

java参数传递 = 值传递

当java传递的参数是基本数据类型时,一个方法不能改变基本数据类型参数的值;
当java传递的参数是引用数据类型时,一个方法可以修改引用数据类型参数所指向的对象的值;
值传递的精髓:传递的是存储单元中的内容,而不是存储单元的引用。

基本数据类型参数

// idea_face-test2-TransferTest
public class TransferTest {public static void main(String[] args) {int num = 1;System.out.println("method()方法调用前=" + num);method(num);System.out.println("method()方法调用后=" + num);}public static void method(int number){number = 2;}//method()方法调用前=1//method()方法调用后=1
}

分析:
在这里插入图片描述
引用数据类型参数

public class TransterTest2 {public static void main(String[] args) {Person p = new Person("汪苏泷");System.out.println("调用方法前= name:" + p.getName());method(p);System.out.println("调用方法后= name:" + p.getName());}public static void method(Person p){p.setName("许嵩");}// 调用方法前= name:汪苏泷// 调用方法后= name:许嵩
}

分析:
在这里插入图片描述

RMI

  • RMI(Remote Method Invocation),远程方法调用,允许运行在一个java虚拟机的对象调用运行在另一个java虚拟机上的对象的方法。两个虚拟机可以是运行在相同计算机上的不同进程,也可以是运行在网络中的不同计算机。
  • RPC(Remote Procedure Call),远程过程调用,用于一个进程调用另一个进程。

Java中的动态代理

代理模式
  • 代理模式(Proxy或Surrogate),就是一个人或机构代替另一个人或机构采取行动。
    一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
    代理就是为其他对象提供一个代理以控制对某个对象的访问。
  • 优点:
    可以隐藏真实目标类的实现;可以实现客户与真实目标类之间的解耦,在不修改真实目标类代码的情况下能够做一些操作;
  • 代理模式实现条件:
    代理类和被代理类实现共同的接口(或继承);
    代理类中存在指向被代理类的索引;( xhj 这个代理类的索引 = 创建的真实类的对象 )
    实际执行是通过调用代理类的方法从而实际执行被代理类的方法。
  • 代理分类:
    静态代理:程序员创建或特定工具自动生成源码,在对其进行编译。运行前,代理类的.class文件就已经存在了;
    动态代理:程序运行时,运用反射机制动态创建而成,动态代理类的字节码文件在程序运行时由java反射机制动态生成,无需程序员手工编写它的源代码。
    • 动态代理分类:
      基于接口的代理:代表 = JDK代理
      基于继承的代理:代表 = CGlib代理
静态代理

实现步骤

1 创建一个接口MyInterface,并创建抽象方法public interface MyInterface{// 创建抽象方法public abstract 返回值类型 method(接口MyInterface参数);}
2 创建MyInterface真实的实现类RealClass,重写接口中的抽象方法public class RealClass implements MyInterface{// 重写接口中的抽象方法,此时方法不在带有abstract修饰符@Overridepublic 返回值类型 method(接口MyInterface参数){真实的实现过程;}}
3 创建 代理类public class ProxyClass implements MyInterface{// 创建接口MyInterface的实现类对象,// 这里创建的是接口MyInterface的真实类对象,也就是代理类中指向被代理类的索引private MyInterface myinterface;// 创建带参构造方法(真实类 真实类对象)ProxyClass(MyInterface myinterface){this.myinterface = myinterface;}// 重写接口MyInterface中的抽象类方法@Overridepublic method(接口MyInterface参数){调用代理方法前做的事情;返回值 x = myinterface.method(接口MyInterface参数);调用代理方法后做的事情;}

理解

  • 好处:
    比如每次从磁盘中获取字体库的时候,磁盘的I/O比较耗时,想通过缓存将读到的字体库暂存一份,直接修改代理类ProxyClass,不用修改真实RealClass类。
    同样这样的需求直接修改RealClass类也可以实现,但是当多个RealClass2,RealClass3也需要判断缓存是否有字体的时候,那么修改真实类就需要修改多个,而采用代理类的形式,只需要修改一个类即可。

代码
需求:考虑一个字体提供功能,字体库可能源自本地磁盘、网络或者系统。

// 326-test1
// 接口
// 先考虑从本地磁盘中获取字体,采用代理的方式实现,定义一个提供字体的接口FontProvider:
public interface FontProvider {Font getFont(String name);
}
// 真实类:真正提供获取磁盘字体库的类
public class FontProviderFromDisk implements FontProvider{@Overridepublic Font getFont(String name) {System.out.println("磁盘上的字体库");return null;}// 只是输出文字,返回值类型可以不写为Font类型,而是写为void
}
// 代理类
public class ProxyForFont implements FontProvider{private FontProvider fontProvider;// 定义一个接口对象,实际上是被代理类对象ProxyForFont(FontProvider fontProvider){this.fontProvider = fontProvider;}// 定义带参构造方法,参数为接口对象@Overridepublic Font getFont(String name) {System.out.println("提供代理方法前做的事情");Font font = fontProvider.getFont(name);System.out.println("调用代理后做的事情");return font;}
}
// 测试类
public class MyFontDemo {public static void main(String[] args) {FontProvider fp = new ProxyForFont(new FontProviderFromDisk());// 多态的形式创建FontProvider接口对象// 接口 对象名 = new 代理类名(new 真实类名());fp.getFont("宋体");}
}
动态代理

出现的原因
当需要获取多种资源,比如字体、图片、音乐等资源,这时就需要多个接口,比如FontProvider、ImageProvider、MusicProvider接口等,需要对应的实现类(比如FontClass、ImageClass、MusicClass等),和其对一个的代理类(比如FontProxy、ImageProxy、MusicProxy等)。当需要给获取字体、图片、音乐等功能上加上缓存功能,就需要对三个代理类进行改动,但是修改的部分逻辑上又相同,就会造成代码的重复和代理类爆炸。
分类: 基于接口的代理(JDK) 和 基于继承的代理(CGlib)

基于接口的代理JDK

InvocationHandler接口

  • InvocationHandler接口
    每个代理类的实例都会有一个与之相关联的InvocationHandler实现类,如果代理的方法被调用,那么代理便会通知和转发给内部的 InvocationHandler 实现类,由它决定处理。
public interface InvocationHandler { // 接口// 唯一的方法invokepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable;
}

参数介绍:

参数名说明
Object proxy代理对象
Method method代理对象调用的方法
Object[] args调用方法中的参数

Proxy类的静态newProxyInstance方法
返回指定接口的代理实例,该代理实例将方法调用分配给指定的调用处理程序。

public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)

参数介绍:

参数名说明获得
ClassLoader loader类加载器测试类.class.getClassLoader()
Class<?>[] interfaces用来代理的接口new Class[]{接口.class}
也就是说这里可以是多个接口.class
InvocationHandler h一个InvocationHandler对象InvocationHandler handler = new jdkProxyClass(new RealSubject());
jdkProxyClass是代理类,RealSubject是真实类

说明

  • 测试类.class.getClassLoader()
    测试类.class,得到该类对应的Class类对象,通过类的class属性
    Class类对象.getClassLoader(),得到Class类对象的类加载器
  • Class<?>[] interfaces = new Class[]{接口.class}
    2022/4/25 理解:需要创建一个Class数组,成员是接口的Class对象。

具体的实现步骤

1 创建接口MyInterfacepublic interface MyInterface{// 定义抽象方法public abstract 返回值类型 method1();public abstract 返回值类型 method2();}2 创建真实的接口实现类RealClasspublic class RealClass{// 重写抽象方法@Overridepublic 返回值类型 method1(){method1具体的实现;}@Overridepublic 返回值类型 method2(){method2具体的实现;}}3 创建接口的代理类,需要实现InvocationHandler接口public class jdkProxyClass implements InvocationHandler{// 创建接口的真实类对象private RealClass realObj;// 创建真实类对象为参数的带参构造方法public jdkProxyClass(RealClass realObj){this.realObj = realObj;}// 重写Object类中的invoke方法public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// proxy 被代理的对象;method:调用的方法;args:方法所需的参数执行代理调用之前的操作。Object result = null;// 这里的方法是通过反射获取的方法对象result = method.invoke(realObj,args);return result;}4 创建测试类public class jdkDemo{public static void main(String[] args){// 创建要代理的真实对象,采用多态的形式MyInterface realObj = new RealClass();// 创建代理对象InvocationHandler handler = new jdkProxyClass(realObj);// 使用Proxy.newProxyInstrace()这个静态方法 获得接口的对象MyInterface myinterface = (MyInterface)Proxy.newProxyInstance(jdkDemo.class.getClassLoader(),new Class[]{MyInterface.class},handler);// 通过接口对象调用方法myinterface.method1();myinterface.method2();}

案例:

// 326-test2
// 接口定义
public interface Subject {void method1();void method2();
}
// 接口真实的实现类定义
public class RealSubject implements Subject{@Overridepublic void method1() {System.out.println("method1执行");}@Overridepublic void method2() {System.out.println("method2执行");}
}// 接口的代理类定义
public class jdkProxySubject implements InvocationHandler {// 创建代理类的真实对象,也就是真实类的对象private Object subject;// 创建带参构造方法public jdkProxySubject(Object subject){this.subject = subject;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// proxy:被代理的对象// method:调用的方法// args:方法所需要的参数System.out.println("before");Object result = null;try{// 利用反射动态的来反射方法,动态代理和静态代理的区别result = method.invoke(subject, args);}catch(Exception e){System.out.println("ex:" + e.getMessage());}finally {System.out.println("after");}return result;}
}
// 测试类
public class Client {public static void main(String[] args){// 创建要代理的真实对象Subject realSubject = new RealSubject();// 创建代理对象,将真实对象作为参数传入InvocationHandler handler =new jdkProxySubject(realSubject);Subject subject = (Subject)Proxy.newProxyInstance(Client.class.getClassLoader(), new Class[]{Subject.class}, handler);subject.method1();subject.method2();}
}
基于继承的代理 CGlib

概述

  • CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,为一个类创建子类,并在子类中采用方法拦截技术 拦截所有对父类方法的调用,并顺势加入横切逻辑。
    ASM是一款java字节码层面的代码分析和修改工具。
    ASM的目标是生成、转换、分析已编译的java class文件,可使用ASM工具读/写/转换JVM指令集。
  • CGlib是针对类来实现代理的,原理是对指定的业务类生成一个子类,并覆盖其中业务方法实现代理,因为采用的是继承,所以不能对final修饰的类进行代理。
    由于spring框架中的MethodInterceptor接口,目前没有学习spring框架 ,后期总结。
    参考博客

这篇关于java35-克隆-浅克隆-深克隆-java参数值传递-RMI-代理模式-静态代理-动态代理JDK-invocationHandler-newProxyInstance的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/693468

相关文章

Java编译生成多个.class文件的原理和作用

《Java编译生成多个.class文件的原理和作用》作为一名经验丰富的开发者,在Java项目中执行编译后,可能会发现一个.java源文件有时会产生多个.class文件,从技术实现层面详细剖析这一现象... 目录一、内部类机制与.class文件生成成员内部类(常规内部类)局部内部类(方法内部类)匿名内部类二、

SpringBoot实现数据库读写分离的3种方法小结

《SpringBoot实现数据库读写分离的3种方法小结》为了提高系统的读写性能和可用性,读写分离是一种经典的数据库架构模式,在SpringBoot应用中,有多种方式可以实现数据库读写分离,本文将介绍三... 目录一、数据库读写分离概述二、方案一:基于AbstractRoutingDataSource实现动态

Linux系统配置NAT网络模式的详细步骤(附图文)

《Linux系统配置NAT网络模式的详细步骤(附图文)》本文详细指导如何在VMware环境下配置NAT网络模式,包括设置主机和虚拟机的IP地址、网关,以及针对Linux和Windows系统的具体步骤,... 目录一、配置NAT网络模式二、设置虚拟机交换机网关2.1 打开虚拟机2.2 管理员授权2.3 设置子

Springboot @Autowired和@Resource的区别解析

《Springboot@Autowired和@Resource的区别解析》@Resource是JDK提供的注解,只是Spring在实现上提供了这个注解的功能支持,本文给大家介绍Springboot@... 目录【一】定义【1】@Autowired【2】@Resource【二】区别【1】包含的属性不同【2】@

springboot循环依赖问题案例代码及解决办法

《springboot循环依赖问题案例代码及解决办法》在SpringBoot中,如果两个或多个Bean之间存在循环依赖(即BeanA依赖BeanB,而BeanB又依赖BeanA),会导致Spring的... 目录1. 什么是循环依赖?2. 循环依赖的场景案例3. 解决循环依赖的常见方法方法 1:使用 @La

Java枚举类实现Key-Value映射的多种实现方式

《Java枚举类实现Key-Value映射的多种实现方式》在Java开发中,枚举(Enum)是一种特殊的类,本文将详细介绍Java枚举类实现key-value映射的多种方式,有需要的小伙伴可以根据需要... 目录前言一、基础实现方式1.1 为枚举添加属性和构造方法二、http://www.cppcns.co

Elasticsearch 在 Java 中的使用教程

《Elasticsearch在Java中的使用教程》Elasticsearch是一个分布式搜索和分析引擎,基于ApacheLucene构建,能够实现实时数据的存储、搜索、和分析,它广泛应用于全文... 目录1. Elasticsearch 简介2. 环境准备2.1 安装 Elasticsearch2.2 J

Linux系统中卸载与安装JDK的详细教程

《Linux系统中卸载与安装JDK的详细教程》本文详细介绍了如何在Linux系统中通过Xshell和Xftp工具连接与传输文件,然后进行JDK的安装与卸载,安装步骤包括连接Linux、传输JDK安装包... 目录1、卸载1.1 linux删除自带的JDK1.2 Linux上卸载自己安装的JDK2、安装2.1

Java中的String.valueOf()和toString()方法区别小结

《Java中的String.valueOf()和toString()方法区别小结》字符串操作是开发者日常编程任务中不可或缺的一部分,转换为字符串是一种常见需求,其中最常见的就是String.value... 目录String.valueOf()方法方法定义方法实现使用示例使用场景toString()方法方法

Java中List的contains()方法的使用小结

《Java中List的contains()方法的使用小结》List的contains()方法用于检查列表中是否包含指定的元素,借助equals()方法进行判断,下面就来介绍Java中List的c... 目录详细展开1. 方法签名2. 工作原理3. 使用示例4. 注意事项总结结论:List 的 contain