一)Java中支持多继承吗,为什么?
答案:在JAVA中是不支持多继承的,原因是多继承会存在菱形继承的问题
菱形继承:
1)菱形继承也被称之为是钻石继承,是一种在JAVA面向对象编程的时候遇到的一个可能出现的继承问题;
2)假设JAVA支持多继承,那么就有可能一个类D继承两个不同的类C和类B,而这两个类最终又继承一个父类A,形成一个钻石或者是菱形的继承关系,这样一来D类就继承了B C A三个类此时就会出现一定的问题:
3)下面这种情况就会发生歧义或者是二义性问题,因为如果A,B,C类都存在着相同的方法,但是D类此时有没有重写这个方法,那么我此时D d=new D(),d.run(),那么在调用这个方法的时候,编译器无法确定应该调用哪一个父类的方法,因为D类此时直接或者间接继承了三个相同的方法,此时就会导致编译错误
二)String底层是怎么实现的?
Java的String类在lang包里面,这个包不需要手动进行导入,是由JAVA程序自动进行导入
1)String底层的实现是基于数组(字节数组或者是字符数组)和字符串常量池来进行实现则,利用不可变final来修饰
2)为什么要把char[]数组变成byte[]数组,就是为了更方便地精细化进行管理,一个char等于两个byte,如果一个汉字要占用三个字节,那么在JDK9之前只能创建大小为2个char,但是多出来一个byte是不会使用的,但是程序没有办法表示三个byte,所以我就只能创建4个byte,这本身就不够精细化,占用内存空间比较大,这个时候再JDK9之后就可以创建大小为3的字节数组来表示一个汉字了
三)为什么String要设计成不可变对象?
1)方便实现字符串常量池,字符串常量池在程序运行的时候可以节省很多内存空间,因为不同的字符串变量指向相同的字面量的时候,都指向字符串常量池中的同一个对象,既节省了空间,又提升了速度
2)不可变对象是线程安全的,String的不可变性保证了字符串对象的值不会被修改,这也就意味着多线程环境下,多个线程可以共享字符串对象而不需要担心它的值被修改,不会存在多个线程同时修改同一个变量的问题;
3)不可变对象更方便的hashcode,作为Key时可以更方便的保存到HashMap里面,因为字符串是不可变的,所以在它创建的时候hashcode就被缓存了,不需要重新计算,更方便的进行缓存
四)String是如何设计成不可变对象的?
String是不可变对象,代表的是字符串中的内容是不可以改变的
1)String类本身被final修饰,表示该类不可被继承
2)String中的char[] val数组被final修饰表明这个val引用不可以指向其它字符数组,但是是可以通过value引用将这个数组中的内容进行修改
3)字符串真正不能被修改的原因是,存储字符串的value是被private进行修饰的,只能在String类中被使用,但是String中没有公开提供访问String中的value的公开方法,拿不到value引用,在类外不可以访问私有的成员变量,所以String初始化之后外界没有有效的手段去改变它
4)所有涉及到修改字符串内容的操作都是创建一个新对象,所以说改变的是新对象
网上有些人说字符串不可变内部是因为内部保存字符的数组是被final所修饰的,final修饰这个类表明这个类是不可以被继承的,final修饰引用表示此引用变量不可以指向其他对象,但是引用对象里面的内容是可以进行修改的
5)在String类的实现中,所有涉及到修改字符串内容的操作都是创建一个新的对象,我们修改的是新对象,是因为 Java 作者在 String 的所有方法里面,都很小心地避免去修改了 char 数组中的数据涉及到对 char 数组中数据进行修改的操作全部都会重新创建一个 String对象
6(就假设拿下面这个代码来说:要避免直接修改String对象,因为String类是不能够直接修改的,所有的修改都会直接创建一个新对象,效率十分低下
在我们的这个代码进行循环拼接的过程中,尤其是我们从反汇编的语法中可以看到,这种方式不推荐使用,可以看到每一次循环都需要重新创建一个StringBuilder对象,效率非常低
每一次进行字符串拼接的过程中,每一次循环都new了一个StringBuilder对象,调用 append方法进行拼接,每一次循环都会new一个StringBuilder对象,最后调用toString()转化成String对象,这频繁的new对象效率就会变得非常低,注意:这里面的init方法就表示构造方法,每一次循环都创建两个对象,效率非常低
每一次字符串的拼接,会被优化成创建StringBuilder对象,最后调用ToString,new对象花费时间,消耗内存