Java基础面试题(5)——每天掌握10道题

目录

1、String有哪些特性?

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

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

4、String和StringBuffer、StringBuilder的区别是什么?

5、int 和 Integer 有什么区别?

6、什么 是内存泄漏和内存溢出,有什么解决办法?

7、final 和static的区别?

8、 jdk动态代理,静态代理讲讲?

9、 什么是序列化和反序列化?序列化的底层怎么实现的?

10、不同JDK版本之间中的字符串调用intern()方法的区别?


1、String有哪些特性?

  • 不变性:String 是只读字符串,是一个典型的 immutable 对象,对它进行任何操作,其实都是创建一个新的对象,再把引用指向该对象。不变模式的主要作用在于当一个对象需要被多线程共享并频繁访问时,可以保证数据的一致性。

  • 常量池优化:String 对象创建之后,会在字符串常量池中进行缓存,如果下次创建同样的对象时,会直接返回缓存的引用。

  • final:使用 final 来定义 String 类,表示 String 类不能被继承,提高了系统的安全性。

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

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

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

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

4、String和StringBuffer、StringBuilder的区别是什么?

可变性

String类中使用字符数组保存字符串,private final char value[],所以string对象是不可变的。StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串,char[] value,这两种对象都是可变的。

线程安全性

String中的对象是不可变的,也就可以理解为常量,线程安全。AbstractStringBuilder是StringBuilder与StringBuffer的公共父类,定义了一些字符串的基本操作,如expandCapacity、append、insert、indexOf等公共方法。StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。

性能

每次对String 类型进行改变的时候,都会生成一个新的String对象,然后将指针指向新的String 对象。StringBuffer每次都会对StringBuffer对象本身进行操作,而不是生成新的对象并改变对象引用。相同情况下使用StirngBuilder 相比使用StringBuffer 仅能获得10%~15% 左右的性能提升,但却要冒多线程不安全的风险。

对于三者使用的总结

如果要操作少量的数据推荐用 =>String

单线程操作字符串缓冲区 下操作大量数据推荐用 => StringBuilder

多线程操作字符串缓冲区 下操作大量数据 推荐用 => StringBuffer

5、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

6、什么 是内存泄漏和内存溢出,有什么解决办法?

内存泄漏:其实就是当我们在创建对象或者是变量之后,并且该对象或者变量在程序中都不再使用了,但是又没有办法回收这些没用的对象和变量。而且还一直占用着系统的内存空间,此时的现象就是内存泄漏。

解决办法:

  • 我们在使用静态变量的时候尽量减少对静态变量的使用,因为静态变量是存储在方法区(也就是永久代),不会被回收

  • 如果在程序中有不用的对象,应该及时进行释放。

内存溢出:其实就是当我要申请一块内存的时候,但是此时虚拟机不能够给我提供这么大的内存,就说明内存溢出了,比如出现OutOfMemoryError,StackOverflowError 这种错误信息的时候

解决办法:

  • 修改 JVM 启动参数, 直接增加内存。 (-Xms, -Xmx 参数一定不要忘记加。一般要将-Xms 和-Xmx 选项设置为相同, 以避免在每次 GC 后调整堆的大小; 建议堆的最大值设置为可用内存的最大值的 80%)。

  • 根据日志进行分析哪里的问题所在,进行排查。

7、final 和static的区别?

先说一下static:分为静态方法、静态变量、静态块:所以静态代码块=静态变量>静态方法(因为静态方法是只有调用的时候才会去执行,否则不去执行)

如果一个类还没有被加载的时候:

  • 会先去加载父类的静态变量和静态代码块(并且静态变量和静态代码块的加载顺序和他们在代码中出现的位置有关)

  • 然后再去加载该类的静态代码块和静态变量的初始化

  • 之后再去加载父类的实例变量的初始化

  • 然后去执行父类的构造函数

  • 再去加载该类的实例变量的初始化

  • 然后去执行该类的构造函数

关于final的用法:

  • 如果当final加在类上,那么这个类就不能再被继承

  • 如果当final加在方法上,那么这个方法就不能被重写。

  • 如果当final加在成员变量或者是局部变量上,那么这个变量的值就不能被修改。(补充:如果加在成员变量上,并且成员变量是一个对象,比如是一个Person对象,其实还是可以继续往Person对象中加一些其他的属性的,但是不能修改这个对象的引用而已,但是对象的内容还是可以改变的

  • final还可以加在方法的参数上,比如加在内部类的参数时就必须使用final关键字进行修饰(在JDK1.8之前都是必须的,但是在JDK1.8之后就可以不加了),因为内部类会持有外部类引用和方法中参数的引用,反编译class文件后,内部类的class文件的构造函数参数 中会显示传入 外部类对象(必然会加)以及方法内局部变量和形参(如果内部类有调用会加),不管是基本数据类型还是引用变量,如果重新赋值了,会导致内外指向的对象不一致,所以java就暴力的规定使用final,不能重新赋值。

8、 jdk动态代理,静态代理讲讲?

静态代理:

  • 其实静态代理的最终目的就是为了扩展原本的对象方法的功能,通过代理对象类在去保证真实对象功能的前提下,再此基础上去扩展一些新的功能或者说是去增加一些繁琐且必须要去做的事情

  • 但是如果当我们的业务接口过多的话,我们的代理对象也会变的很多,而且代理对象和真实对象都必须要实现同一个真正干活的业务接口。这也是他的缺点

jdk的动态代理:

  • 而其实动态代理就是为了解决上一个静态代理的缺点,他可以不用代理对象和真是对象都去同时实现同一个业务接口。

  • 而动态的代理的核心就是实现InvocationHandler 接口类重写invoke方法Object invoke(Object proxy, Method method, Object[] args) :在代理实例上处理方法调用并返回结果。 和public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h) 这两个核心方法。

  • 还有一点核心的就是使用ava.lang.reflect.Proxy类,他提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类Object invoke(Object proxy, Method method, Object[] args)。Ojbect proxy:表示需要代理的对象,Method method:表示要操作的方法, Object[] args:method方法所需要传入的参数(可能没有为,null.也可能有多个)

9、 什么是序列化和反序列化?序列化的底层怎么实现的?

什么是序列化和反序列化?

  • Java序列化是指把Java对象保存为二进制字节码的过程,Java反序列化是指把二进制码重新转换成Java对象的过程

序列化的底层怎么实现的?

其实就是利用了java的IO流来进行实现的

代码示例:

 public static void main(String args[]) throws IOException {  
  
        FileOutputStream fos = new FileOutputStream("D:/demo.text");  
  
        ObjectOutputStream oos = new ObjectOutputStream(fos);  
  
     //这个是一个需要序列化的实体对象
        User user = new User();  
     //这是为了把实体对象写到输出流中,然后保存到D盘的一个路径下
        oos.writeObject(user);  
  
        oos.flush();  
  
        oos.close();  
  
 } 

10、不同JDK版本之间中的字符串调用intern()方法的区别?

  • 首先字符串中的intern()在JDK1.6及以前的时候,如果String s = new String(“wangwei”)这样一个字符串的话,那么如果s调用了intern()方法的时候(其实就是想把堆中的字符串放一个副本到常量池中去【这个副本和真实堆中的字符串是不一样的】),那么就会先去常量池中看wangwei这个字符串是不是存在,如果存在直接返回常量池中的引用(其实就是没有真正的放进去),如果常量池不存在的话,那么会把s这个字符串对象添加到常量池中一份

    如果下面的代码在JDK1.6及以前的话,下面的代码全部是false

  • 在JDK1.6以后的时候,同样的字符串String s = new String(“wangwei”),如果s调用了intern()方法的时候,同样也会先去看常量池中是不是有wangwei这个字符串,如果有直接返回字符串的引用,但是如果没有的话,那么会再去看该对象是不是在堆中已经存在,若果存在【也】会把堆中对象的引用添加到常量池中,并返回该引用,如果堆中不存在,那么会在常量池创建wangwei这个对象并返回其引用。 

  • 代码示例:

  •   //这个是在JDK中1.8中测试的
         public static void main(String[] args) {
            String string = new String("wangwei");
             string.intern();
             String str2 = "wangwei";
             System.out.println(string == str2); //false
     ​
             String  str3 = new String("wang") + new String("wei123");
             str3.intern();
             String str4 = "wangwei123";
             System.out.println(str3 == str4); //true
         }

     

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页