Java面试题汇总 | 基础知识第九章

上一章 :Java面试题汇总 | 基础知识第八章-CSDN博客

目录

反射

什么是反射机制?

反射机制优缺点?

反射机制的应用场景有哪些?

Java获取反射的三种方法?

常用API

String相关

字符型常量和字符串常量的区别?

什么是字符串常量池?

String 是最基本的数据类型吗?        

String有哪些特性?

String为什么是不可变的?

String真的是不可变的吗?

是否可以继承 String 类?

String str="i"与 String str=new String(“i”)一样吗?

在String s = new String(“xyz”);中,创建了几个字符串对象?

如何将字符串反转?

数组有没有 length()方法?String 有没有 length()方法?

String 类的常用方法都有那些?

在使用 HashMap 的时候,用 String 做 key 有什么好处?

String和StringBuffer、StringBuilder的区别是什么?(之前也提到过)

可变性

线程安全性

性能

对于三者使用的总结

Date相关

包装类相关

自动装箱与拆箱是什么?

int 和 Integer 有什么区别?

Integer a= 127 与 Integer b = 127相等吗?


反射

什么是反射机制?

        JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

        补充 :静态编译和动态编译
                静态编译:在编译时确定类型,绑定对象
                动态编译:运行时确定类型,绑定对象

反射机制优缺点?

        优点: 运行期类型的判断,动态加载类,提高代码灵活度。
        缺点: 性能瓶颈:反射相当于一系列解释操作,通知 JVM 要做的事情,性能较差。

反射机制的应用场景有哪些?

        反射是框架设计的灵魂。
        在我们平时的项目开发过程中,基本上很少会直接使用到反射机制,但这不能说明反射机制没有用,实际上有很多设计、开发都与反射机制有关,例如模块化的开发,通过反射去调用对应的字节码;动态代理设计模式也采用了反射机制,还有我们日常使用的 Spring/Hibernate 等框架也大量使用到了反射机制。
举例:
         ①我们在使用JDBC连接数据库时使用Class.forName()通过反射加载数据库的驱动程序;
        ②Spring框架也用到很多反射机制,最经典的就是xml的配置模式。
        Spring 通过 XML 配置模式装载 Bean 的过程:
                1) 将程序内所有 XML 或Properties 配置文件加载入内存中;
                2)Java类里面解析xml或properties里面的内容,得到对应实体类的字节码字符串以及相                     关的属性信息;
                3)使用反射机制,根据这个字符串获得某个类的Class实例;
                4)动态配置实例的属性。

Java获取反射的三种方法?

        1.通过new对象实现反射机制 2.通过路径实现反射机制 3.通过类名实现反射机制
示例代码:
public class Student{
    private int id; 
    String name;
    protected boolean sex;
    public float score;
  }
public class Get{
//获取反射机制三种方式
    public static void main(String[] args) throws ClassNotFoundException {
    //方式一(通过建立对象) 
        Student stu = new Student(); 
        Class classobj1 = stu.getClass(); 
        System.out.println(classobj1.getName());
    //方式二(所在通过路径-相对路径) 
        Class classobj2 = Class.forName("fanshe.Student");
        System.out.println(classobj2.getName());
    //方式三(通过类名) 
        Class classobj3= Student.class;
        System.out.println(classobj3.getName());
    }
}

常用API

String相关

字符型常量和字符串常量的区别?

        1. 形式上: 字符常量是单引号引起的一个字符 字符串常量是双引号引起的若干个字符

        2. 含义上: 字符常量相当于一个整形值(ASCII值),可以参加表达式运算 字符串常量代表一个地                           址值(该字符串在内存中存放位置)
        3. 占内存大小 字符常量只占一个字节 字符串常量占若干个字节(至少一个字符结束标志)

什么是字符串常量池?

        字符串常量池位于堆内存中,专门用来存储字符串常量,可以提高内存的使用率,避免开辟多块空间存储相同的字符串。在创建字符串时 JVM 会首先检查字符串常量池,如果该字符串已经存在池中,则返回它的引用,如果不存在,则实例化一个字符串放到池中,并返回其引用。

String 是最基本的数据类型吗?        

        不是。Java 中的基本数据类型只有 8 个 :byte、short、int、long、float、double、char、boolean;除了基本类型(primitive type),剩下的都是引用类型(referencetype),Java 5 以后引入的枚举类型也是一种比较特殊的引用类型。
        
     加餐小tips:
         基本数据类型中用来描述文本数据的是 char,但是它只能表示单个字符,比如 ‘a’,‘好’ 之类的,如果要描述一段文本,就需要用多个char 类型的变量,也就是一个 char 类型数组,比如“你好” 就是长度为2的数组 char[] chars = {‘你’,‘好’};但是使用数组过于麻烦,所以就有了 String,String 底层也是一个 char 类型的数组,只是使用的时候开发者不需要直接操作底层数组,用更加简便的方式即可完成对字符串的使用。

String有哪些特性?

        不变性:String 是只读字符串,是一个典型的 immutable (不可变的)对象,对它进行任何操作,其实都是创建一个新的对象,再把引用指向该对象。不变模式的主要作用在于当一个对象需要被多线程共享并频繁访问时,可以保证数据的一致性。
        常量池优化:String 对象创建之后,会在字符串常量池中进行缓存,如果下次创建同样的对象时,会直接返回缓存的引用。
        final:使用 final 来定义 String 类,表示 String 类不能被继承,提高了系统的安全性。

String为什么是不可变的?

        简单来说就是String类利用了final修饰的char类型数组存储字符,源码如下:
/** The value is used for character storage. */private final char value[];

String真的是不可变的吗?

        我觉得如果别人问这个问题的话,回答不可变就可以了。 下面只是给大家看两个有代表性的例子:
1) String不可变但不代表引用不可以变
例如:
String str ="Hello";
str = str +" World";
System.out.println("str="+ str);
结果:str=Hello World
解析:实际上原先String的内容并没有发生改变,只是str由原来指向"Hello"的内存地址转为指向"Hello World"的内存地址而已,也就是说Java重新开辟了一块内存区域用于存储"Hello World"字符串。
2) 通过反射是可以修改所谓的“不可变”对象
例如:
// 创建字符串"Hello World",并赋给引用s
String s = "Hello World";
System.out.println("s = " + s); // 输出:s = Hello World

// 获取String类中的value字段
Field valueFieldOfString = String.class.getDeclaredField("value");
// 改变value属性的访问权限
valueFieldOfString.setAccessible(true);

// 获取s对象上的value属性的值
char[] value = (char[]) valueFieldOfString.get(s);

// 改变value所引用的数组中的第5个字符
value[5] = '_';

System.out.println("s = " + s); // 输出:s = Hello_World
结果:
s = Hello Worlds = Hello_World
解析:
        用反射访问私有成员, 然后反射出String对象中的value属性, 进而改变通过获得的value引用改变数组的结构。但是一般我们不会这么做(破坏了原有代码结构的完整性)。

是否可以继承 String 类?

        String 类是 final 类,不可以被继承。

String str="i"与 String str=new String(“i”)一样吗?

         不一样,因为内存的分配方式不一样。String str="i"的方式,java 虚拟机会将其分配到常量池中;而 String str=new String(“i”) 则会被分到堆内存中。

在String s = new String(“xyz”);中,创建了几个字符串对象?

        两个对象,一个是静态区的"xyz",一个是用new创建在堆上的对象。

代码解析:

String str1 = "hello"; // str1指向静态区
String str2 = new String("hello"); // str2指向堆上的对象

    System.out.println(str1.equals(str2)); // true

    System.out.println(str2.equals(str4)); // true

    System.out.println(str1 == str3); // true

    System.out.println(str1 == str2); // false

    System.out.println(str2 == str4); // false

    System.out.println(str2.equals("hello")); // false

    str2 = str1;
    System.out.println(str2 == "hello"); // true

如何将字符串反转?

        使用 StringBuilder 或者 stringBuffer 里的 reverse() 方法。
加餐小tips:
        StringBuffer和StringBuilder都用于构建和操作字符串,但StringBuilder是非线程安全的,而StringBuffer是线程安全的。在每次操作字符串时,StringBuilder会生成一个新的字符串,而StringBuffer则不会。因此,StringBuilder在单线程环境下通常会比StringBuffer更高效。
示例代码:
// 使用StringBuffer翻转字符串
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("abcdefg");
System.out.println(stringBuffer.reverse()); // 输出: gfedcba

// 使用StringBuilder翻转字符串
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("abcdefg");
System.out.println(stringBuilder.reverse()); // 输出: gfedcba

数组有没有 length()方法?String 有没有 length()方法?

         数组没有 length()方法 ,但有 length 的属性。String 有 length()方法。而JavaScript中,获得字符串的长度是通过 length属性得到的,这一点容易和 Java 混淆。

String 类的常用方法都有那些?

        indexOf():返回指定字符的索引
        charAt():返回指定索引处的字符
        replace():字符串替换
        trim():去除字符串两端空白
        split():分割字符串,返回一个分割后的字符串数组
        getBytes():返回字符串的 byte 类型数组
         length():返回字符串长度
        toLowerCase():将字符串转成小写字母
        toUpperCase():将字符串转成大写字符
         substring():截取字符串
        equals():字符串比较

在使用 HashMap 的时候,用 String 做 key 有什么好处?

        HashMap 内部实现是通过 key 的 hashcode 来确定 value 的存储位置,因为字符串是不可变的,所以当创建字符串时,它的 hashcode 被缓存下来,不需要再次计算,所以相比于其他对象更高效。

String和StringBuffer、StringBuilder的区别是什么?(之前也提到过)

可变性
        String类中使用字符数组保存字符串,所以string对象不可变。而StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串,但这两种对象都是可变的,因为它们在内存中维护了一个可变的字符数组,并且提供了一系列的方法来修改这个数组的内容。当我们使用这些方法时,它们会直接在内存中进行修改,而不会像String类那样生成一个新的字符串。
线程安全性
        String中的对象是不可变的,也就可以理解为常量,线程安全。AbstractStringBuilder是StringBuilder与StringBuffer的公共父类,定义了一些字符串的基本操作,如expandCapacity、append、insert、indexOf等公共方法。
        StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。
性能
        每次对String 类型进行改变的时候,都会生成一个新的String对象,然后将指针指向新的String 对象。StringBuffer每次都会对StringBuffer对象本身进行操作,而不是生成新的对象并改变对象引用。相同情况下使用StirngBuilder 相比使用StringBuffer 仅能获得10%~15% 左右的性能提升,但却要冒多线程不安全的风险。
对于三者使用的总结
如果要操作少量的数据用 = String
单线程操作字符串缓冲区 下操作大量数据 = StringBuilder
多线程操作字符串缓冲区 下操作大量数据 = StringBuffer

Date相关

包装类相关

自动装箱与拆箱是什么?

        装箱:将基本类型用它们对应的引用类型包装起来;
        拆箱:将包装类型转换为基本数据类型;

int 和 Integer 有什么区别?

         Java 是一个近乎纯洁的面向对象编程语言,但是为了编程的方便还是引入了基本数据类型,但是为了能够将这些基本数据类型当成对象操作,Java 为每一个基本数据类型都引入了对应的包装类型(wrapper class),int 的包装类就是 Integer,从 Java 5 开始引入了自动装箱/拆箱机制,使得二者可以相互转换。
Java 为每个原始类型提供了包装类型:
         原始类型: boolean,char,byte,short,int,long,float,double
        包装类型:Boolean,Character,Byte,Short,Integer,Long,Float,Double

Integer a= 127 与 Integer b = 127相等吗?

对于对象引用类型:==比较的是对象的内存地址。
对于基本数据类型:==比较的是值。
        如果整型字面量的值在-128到127之间,那么自动装箱时不会new新的Integer对象,而是直接引用常量池中的Integer对象,超过范围 a1==b1的结果是false。
public class Main {
    public static void main(String[] args) {
        Integer a = new Integer(3); // 创建一个Integer对象,值为3
        Integer b = 3; // 自动装箱,将整数3转换为Integer类型
        // 比较引用,而不是值
        System.out.println(a == b); // false,因为两个引用指向不同的对象

        int c = 3; // 声明一个int类型的变量,值为3
        // a自动拆箱成int类型再和c比较
        System.out.println(a == c); // true,因为自动拆箱后比较的是值

        // 比较两个整数值,这两个对象引用相同
        System.out.println(b == c); // true

        // 在这个例子中,即使值相同,但由于Integer的缓存机制,
        // 对象引用可能不同(取决于值是否在-128到127的范围内)
        Integer a1 = 128; // 超出缓存范围,创建新对象
        Integer b1 = 128; // 同上,创建新对象
        System.out.println(a1 == b1); // false,因为两个引用指向不同的对象

        // 在这个例子中,由于值在缓存范围内,JVM会返回相同的对象引用
        Integer a2 = 127; // 创建对象,如果值在缓存范围内,和下面的一样
        Integer b2 = 127; // 创建对象,和上面的一样
        System.out.println(a2 == b2); // true,因为两个引用指向同一对象
    }
}

  • 19
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值