Java中String类中常用的方法(附带相关面试题)

简介: 1.了解反射,2.Class类的三种实例化方法,3.反射机制与对象实例化,4.反射与单例设计模式,5.通过反射获取类结构的信息

1.了解反射

什么是反射,反射有什么作用

1.在Java中,反射是一种机制,允许程序在运行时动态地获取、使用和修改类的信息。通过反射,可以在编译时不知道类的具体信息的情况下,操作和查看类的属性、方法和构造函数等。

2.反射有以下几个主要的作用:

  1. 动态加载类:使用反射可以在运行时动态地加载需要使用的类,而不需要在编译时将类写死在代码中。这样可以实现更加灵活的代码结构和功能。
  2. 获取类的信息:通过反射,可以获取类的名称、父类、接口、方法、字段等详细信息。这对于编写通用框架、调试工具和JavaBean的序列化等场景非常有用。
  3. 创建对象和执行方法:使用反射可以动态地创建对象,即使在编译时无法确定具体的类。同时,还可以在运行时调用任意对象的方法,甚至是私有方法。
  4. 修改私有字段:反射允许程序访问和修改类的私有字段的值。这在某些特定的应用场景中可能是必要的,但需要小心使用,遵循权限和安全性的原则。

需要注意的是,反射是一种强大而复杂的机制,在普通的业务代码中并不常用。滥用反射可能会导致性能下降,代码可读性降低,并增加出错的可能性。因此,在使用反射时,需要谨慎权衡利弊,并确保了解其使用方式和限制。

面试题:什么是反射?

  1. 所谓反射,是java在运行时进行自我观察的能力,通过class、constructor、field、method四个方法获取一个类的各个组成部分。
  2. 在Java运行时环境中,对任意一个类,可以知道类有哪些属性和方法。这种动态获取类的信息以及动态调用对象的方法的功能来自于反射机制。


2.Class类的三种实例化方法

1.通过实例化对象调用getclass()方法实现

2.通过直接调用类名的.Class

3.通过调用Class.forname("包+类名") throws ClassNoFoundException

实例化案例:


packageExample1701;
classMember{
privateStringname;
privateintage;
publicvoidsetName(Stringname) {
this.name=name;
    }
publicvoidsetAge(intage) {
this.age=age;
    }
publicStringgetName() {
returnname;
    }
publicintgetAge() {
returnage;
    }
}
publicclassjavaDemo {
publicstaticvoidmain(String[] args)throwsException {
Membermem=newMember();
//        通过实例化getClass()方法得到类Class<?>claszz1=mem.getClass();
System.out.println(claszz1);
//        通过直接获取类名实例化ClassClass<?>claszz2=Member.class;
System.out.println(claszz2);
//        通过调用Class里的forname方法实现实例化Class<?>claszz3=Class.forName("Example1701.Member");
System.out.println(claszz3);
    }
}

image.gif

3.反射机制与对象实例化

通过反射机制进行对象实例化就能替代new的关键词的使用

案例代码:实例化对象

package Example1702;
class Member{
    Member(){
        System.out.println("自动调用构造函数");
    }
    @Override
    public String toString() {
        return "实现Member对象的创建";
    }
}
public class javaDemo {
    public static void main(String[] args)throws Exception {
        Class<?> claszz = Class.forName("Example1702.Member");
//        创建实例化对象并用Object类进行接收
        Object obj = claszz.getDeclaredConstructor().newInstance();
        System.out.println(obj);
//        对比区别
        Object obj2 = claszz.newInstance();
        System.out.println(obj2);
    }
}

image.gifimage.gif问: 可以发现Object接收两个对象输出后都是一样的,那么

Object obj = claszz.getDeclaredConstructor().newInstance();与

Object obj2 = claszz.newInstance();有什么区别吗

答:

在Java 9及之前,我们可以使用Class.newInstance()方法来创建一个类的实例对象。这个方法是通过调用类的默认构造函数来创建对象的。例如,claszz.newInstance()会调用Member类的默认构造函数创建对象。

然而,从Java 9开始,Class.newInstance()方法被标记为过时了,因为它在处理异常和对私有构造函数的访问上存在一些限制。取而代之的是,推荐使用getDeclaredConstructor().newInstance()方法来创建对象。这个方法更为灵活,可以处理带参数的构造函数并且可以处理私有构造函数。

所以,Object obj = claszz.getDeclaredConstructor().newInstance()Object obj2 = claszz.newInstance()的区别在于:

  1. 创建实例对象的方式不同:claszz.getDeclaredConstructor().newInstance()可以处理带参数的构造函数,而claszz.newInstance()只能调用无参构造函数。
  2. 访问权限不同:claszz.getDeclaredConstructor().newInstance()可以处理私有构造函数,而claszz.newInstance()无法处理私有构造函数。
  3. 兼容性不同:claszz.getDeclaredConstructor().newInstance()是在Java 9及之后引入的,而claszz.newInstance()是在Java 8及之前推荐使用的方法。

因此,在现代的Java版本中,建议使用getDeclaredConstructor().newInstance()方法来创建类的实例对象,它更加通用和灵活。

4.反射与单例设计模式

单例设计模式都不陌生,在以前的文章中有提到过,其实现的方法就是通过私有化构造方法实现外部无法实例化对象,并且在类的内部就定义一个唯一的对象,最后通过函数调用的形式将对象输出出去,并且设计模式有两种一种饿汉模式,一种懒汉模式,分别是饿汉:一开始就定义一个唯一对象,懒汉模式:当需要用到的时候才进行创建对象。

那么懒汉模式下就有可能出现问题,什么问题呢?关于多线程问题,懒汉是通过if判断是否对象为空,但是多线程可能出现并发问题,大家都同时执行了if判断语句发现对象为空则大家都一起创建一个对象,造成对象不唯一,不符合单例设计模式

那么如何解决呢?通过反射就能实现多线程下单例设计模式的唯一性

案例代码:

package Example1703;
class Only {
//    实现单例化设计模式
    private Only(){
    }
    private static volatile Only onlyOne = null;
//    创建对象或者获取对象
    public static  Only getOnly(){
        if (onlyOne == null){
            synchronized (Only.class){
                if (onlyOne == null){
                    try {
//                        通过反射创建对象
                        onlyOne = Only.class.getDeclaredConstructor().newInstance();
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                    return onlyOne;
                }
            }
        }
        return onlyOne;
    }
    public void print(){
        System.out.println("Hello");
    }
}
public class javaDemo {
    public static void main(String[] args) {
        //        创建多线程
        for (int i = 0;i<3;i++){
            new Thread(()->{
                Only.getOnly().print();
                System.out.println(Thread.currentThread().getName());
            },i+"的线程").start();
        }
    }
}

image.gif问1:synchronized (Only.class)的作用,为什么要写Only.class而不是this?

synchronized (Only.class)使用类级别的锁来实现同步。

在Java中,每个类都有一个对应的Class对象,可以通过类名后面加上.class来获取该类的Class对象。而this关键字代表当前实例对象,它是指向当前对象的引用。

在单例模式中,我们希望通过synchronized来保证只有一个线程能够创建实例对象。使用synchronized (Only.class),即使用类级别的锁,意味着多个线程在访问这段代码时会竞争同一个锁,即类级别的锁。

而如果使用synchronized (this),则表示使用实例级别的锁。在这个例子中,我们不希望通过实例级别的锁来控制多个线程对实例的访问,因为还没有创建实例对象,所以也不存在实例对象来获得锁。

因此,为了确保在多线程环境下只创建一个实例对象,需要使用类级别的锁,即synchronized (Only.class),而不是实例级别的锁。

问2:为什么要进行两次判断是否为空?

答: 问2:进行两次判断是否为空的目的是为了提高代码执行的效率和性能。在双重检查锁定机制中,第一次判断onlyOne是否为空是为了避免重复进行同步块的加锁和解锁操作,从而提高了代码的执行效率。如果只有一次判断是否为空,那么每次调用getOnly()方法时都会进入同步块,性能开销会增加。

 同时,第二次判断是为了在多个线程同时通过了第一次判断后,只有第一个获得锁的线程才会进入同步块创建实例。其他线程在获取到锁后,再次判断onlyOne是否为空,如果已经不为空,就直接返回已经创建好的实例,避免了重复创建对象。

如果只有一次判断是否为空,那么即使已经有线程创建了实例,其他线程也会通过第一次判断,进入同步块再次创建实例,这样会造成多个实例的创建,不符合单例模式的要求。

因此,为了确保只有一个线程创建实例,并提高代码的执行效率和性能,需要进行两次判断是否为空。

5.通过反射获取类结构的信息

常用通过反射获取类结构的方法有这几个

方法名
作用
getName() 获取类的完整名称(包括包路径)
getPackage() 获取类所在的包信息
getSuperclass() 获取类的父类
getInterfaces() 获取类实现的接口列表

案例代码:

packageExample1704;
interfaceInterface1 {}
interfaceInterface2 {}
abstractclassFather {}
classTestextendsFatherimplementsInterface1, Interface2 {
privateStringname;
privateintage;
Test(Stringname, intage) {
this.age=age;
this.name=name;
    }
publicvoidprint() {
System.out.println("输出信息Test类");
    }
}
publicclassjavaDemo {
publicstaticvoidmain(String[] args) {
//        获取指定类的class对象Class<?>clazz=Test.class;
//        获取指定类的包的名称Packagepag=clazz.getPackage();
System.out.println(pag.getName());
//        获取类的父类的信息Class<?>superClass=clazz.getSuperclass();
System.out.println(superClass.getName());
//        获取类的接口的信息Class<?>[] interfaces=clazz.getInterfaces();
for (Class<?>temp : interfaces) {
System.out.println(temp.getName());
        }
    }
}

image.gifimage.gif编辑

目录
相关文章
|
20天前
|
存储 编译器 C语言
【c++丨STL】string类的使用
本文介绍了C++中`string`类的基本概念及其主要接口。`string`类在C++标准库中扮演着重要角色,它提供了比C语言中字符串处理函数更丰富、安全和便捷的功能。文章详细讲解了`string`类的构造函数、赋值运算符、容量管理接口、元素访问及遍历方法、字符串修改操作、字符串运算接口、常量成员和非成员函数等内容。通过实例演示了如何使用这些接口进行字符串的创建、修改、查找和比较等操作,帮助读者更好地理解和掌握`string`类的应用。
30 2
|
1月前
|
存储 Java 程序员
Java基础的灵魂——Object类方法详解(社招面试不踩坑)
本文介绍了Java中`Object`类的几个重要方法,包括`toString`、`equals`、`hashCode`、`finalize`、`clone`、`getClass`、`notify`和`wait`。这些方法是面试中的常考点,掌握它们有助于理解Java对象的行为和实现多线程编程。作者通过具体示例和应用场景,详细解析了每个方法的作用和重写技巧,帮助读者更好地应对面试和技术开发。
106 4
|
2月前
|
Java
【编程基础知识】(讲解+示例实战)方法参数的传递机制(值传递及地址传递)以及String类的对象的不可变性
本文深入探讨了Java中方法参数的传递机制,包括值传递和引用传递的区别,以及String类对象的不可变性。通过详细讲解和示例代码,帮助读者理解参数传递的内部原理,并掌握在实际编程中正确处理参数传递的方法。关键词:Java, 方法参数传递, 值传递, 引用传递, String不可变性。
65 1
【编程基础知识】(讲解+示例实战)方法参数的传递机制(值传递及地址传递)以及String类的对象的不可变性
|
2月前
|
安全 Java 测试技术
Java零基础-StringBuffer 类详解
【10月更文挑战第9天】Java零基础教学篇,手把手实践教学!
45 2
|
2月前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
26 1
|
2月前
|
数据可视化 Java
让星星月亮告诉你,通过反射创建类的实例对象,并通过Unsafe theUnsafe来修改实例对象的私有的String类型的成员属性的值
本文介绍了如何使用 Unsafe 类通过反射机制修改对象的私有属性值。主要包括: 1. 获取 Unsafe 的 theUnsafe 属性:通过反射获取 Unsafe类的私有静态属性theUnsafe,并放开其访问权限,以便后续操作 2. 利用反射创建 User 类的实例对象:通过反射创建User类的实例对象,并定义预期值 3. 利用反射获取实例对象的name属性并修改:通过反射获取 User类实例对象的私有属性name,使用 Unsafe`的compareAndSwapObject方法直接在内存地址上修改属性值 核心代码展示了详细的步骤和逻辑,确保了对私有属性的修改不受 JVM 访问权限的限制
60 4
|
2月前
|
JSON 调度 数据库
Android面试之5个Kotlin深度面试题:协程、密封类和高阶函数
本文首发于公众号“AntDream”,欢迎微信搜索“AntDream”或扫描文章底部二维码关注,和我一起每天进步一点点。文章详细解析了Kotlin中的协程、扩展函数、高阶函数、密封类及`inline`和`reified`关键字在Android开发中的应用,帮助读者更好地理解和使用这些特性。
33 1
|
2月前
|
存储 安全 Java
【一步一步了解Java系列】:认识String类
【一步一步了解Java系列】:认识String类
28 2
|
2月前
|
C语言 C++
C++番外篇——string类的实现
C++番外篇——string类的实现
21 0
|
3月前
|
Java 索引
java基础(13)String类
本文介绍了Java中String类的多种操作方法,包括字符串拼接、获取长度、去除空格、替换、截取、分割、比较和查找字符等。
44 0
java基础(13)String类