面试题整理-Java部分(一)

作者 : 开心源码 本文共6641个字,预计阅读时间需要17分钟 发布时间: 2022-05-12 共170人阅读

一.Java部分(一)

1.String、StringBuffer、StringBuilder区别

String:字符串常量;StringBuffer:字符串变量 (线程安全);StringBuilder:字符串变量。
String类中使用字符数组保存字符串,由于有“final”修饰符,所以string对象是不可变的。private final char value[];
StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串,可知这两种对象都是可变的。char[] value;
String 是不可变的对象, 因而在每次对 String 类型进行改变的时候其实都等同于生成了一个新的 String 对象,而后将指针指向新的 String 对象,所以经常改变内容的字符串最好不要用 String

  • 1.每当我们创立字符串常量时,JVM会首先检查字符串常量池,假如该字符串已经存在常量池中,那么就直接返回常量池中的实例引用。假如字符串不存在常量池中,就会实例化该字符串并且将其放到常量池中。
  • 2.从sub操、concat还是replace方法的源码看出以上操作都不是在原有的字符串上进行的,而是重新生成了一个新的字符串对象。也就是说进行这些操作后,最原始的字符串并没有被改变。“String对象一旦被创立就是固定不变的了,对String对象的任何改变都不影响到原对象,相关的任何change操作都会生成新的对象”。
String str1="aaa";String str2="aaa";System.out.println(str1==str2);//true 可以看出str1跟str2是指向同一个对象 ,“aaa”存在常量池中String str3=new String("aaa");String str4=new String("aaa");System.out.println(str3==str4);//false 可以看出用new的方式是生成不同的对象 ,存放在堆中String s1="helloworld";String s2="hello"+"world";System.out.println(s1==s2); //true 可以看出s1跟s2是指向同一个对象 ,"helloworld”是字符串常量,它在编译期就被确定了;而"hello”和"world”也都是字符串常量//当一个字符串由多个字符串常量连接而成时,它自己一定也是字符串常量,所以s2也同样在编译期就被解析为一个字符串常量,所以s2也是常量池中"helloworld”的一个引用。String s0="helloworld"; String s1=new String("helloworld"); String s2="hello" + new String("world"); System.out.println( s0==s1 ); //false  System.out.println( s0==s2 ); //false System.out.println( s1==s2 ); //false//用new String() 创立的字符串不是常量,不能在编译期就确定,所以new String() 创立的字符串不放入常量池中,它们有自己的地址空间String str1="abc";   String str2="def";   String str3=str1+str2;System.out.println(str3=="abcdef"); //false//JVM对String str="abc"对象放在常量池中是在编译时做的,而String str3=str1+str2是在运行时刻才能知道的。new对象也是在运行时才做的。//而这段代码总共创立了5个对象,字符串池中两个、堆中三个。+运算符会在堆中建立来两个String对象,这两个对象的值分别是"abc"和"def",//也就是说从字符串池中复制这两个值,而后在堆中创立两个对象,而后再建立对象str3,而后将"abcdef"的堆地址赋给str3。

总的来说就是:字面量”+”拼接是在编译期间进行的,拼接后的字符串存放在字符串池中;而字符串引用的”+”拼接运算实在运行时进行的,新创立的字符串存放在堆中。关注两个方面:1.存放的位置(堆还是常量池)2.什么时期确定(编译时期还是运行时期)

2.int与integer的区别

1.int是java的一种基本数据类型,Integer是int的包装类。
2.Integer实际是对象的引用,当new一个Integer时,实际上是生成一个指针指向此对象;而int则是直接存储数据值
3.Integer的默认值是null,int的默认值是0

关于Integer和int的比较:

//因为Integer变量实际上是对一个Integer对象的引用,所以两个通过new生成的Integer变量永远是不相等的Integer i = new Integer(100);Integer j = new Integer(100);System.out.print(i == j); //false//由于包装类Integer和基本数据类型int比较时,java会自动拆包装为int,而后进行比较,实际上就变为两个int变量的比较Integer i = new Integer(100);int j = 100;System.out.print(i == j); //true//由于非new生成的Integer变量指向的是java常量池中的对象,而new Integer()生成的变量指向堆中新建的对象,两者在内存中的地址不同Integer i = new Integer(100);Integer j = 100;System.out.print(i == j); //false//对于两个非new生成的Integer对象,进行比较时,假如两个变量的值在区间-128到127之间,则比较结果为true//假如两个变量的值不在此区间,则比较结果为falseInteger i = 100;Integer j = 100;System.out.print(i == j); //trueInteger i = 128;Integer j = 128;System.out.print(i == j); //falsejava在编译Integer i = 100 ;时,会翻译成为Integer i = Integer.valueOf(100);,而java API中对Integer类型的valueOf的定义如下:public static Integer valueOf(int i){    assert IntegerCache.high >= 127;    if (i >= IntegerCache.low && i <= IntegerCache.high){        return IntegerCache.cache[i + (-IntegerCache.low)];    }    return new Integer(i);}java对于-128到127之间的数,会进行缓存,Integer i = 127时,会将127进行缓存,下次再写Integer j = 127时,就会直接从缓存中取,就不会new了,否则new新的对象。

3.java中==和equals和hashCode的区别

基本数据类型的==比较的值相等;类的==比较的内存的地址,即能否是同一个对象,在不覆盖equals的情况下,默认比较内存地址,原实现也为 == 。
equal 是比照两个两个对象能否是等价关系。等价不同于相等。它在不同的类中有不同的规则,equal在不同的类中有不同的重载。如String等重写了equals方法,比较字符串内容相同。
hashCode也是Object类的一个方法。返回一个离散的int型整数。在集合类操作中使用,为了提高查询速度。

  • 假如两个对象equals,Java运行时环境会认为他们的hashcode肯定相等。(两个equals为true的实例必需返回相同的hashCode。)
  • 假如两个对象不equals,他们的hashcode有可能相等。
  • 假如两个对象hashcode相等,他们不肯定equals。(hashCode相同的两个实例,equals方法不肯定会返回true)
  • 假如两个对象hashcode不相等,他们肯定不equals。

4.谈谈对java多态的了解

多态:不同的对象对同一种行为具备不同的体现。
前提条件:1.继承,2.重写,3.父类引用指向子类实例(上转型)
所谓多态就是:指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定。由于在程序运行时才确定具体的类,这样,不用修改源程序代码,即可以让引用变量绑定到各种不同的类实现上,从而导致该引用调用的具体方法随之改变,让程序可以选择多个运行状态,这就是多态性。
指向子类的父类引用因为向上转型了,它只能访问父类中拥有的方法和属性,若子类重写了父类中的某些方法,在调用该些方法的时候,必定是使用子类中定义的这些方法(动态连接、动态调用)。

5.什么是内部类?内部类的作用

定义:将一个类定义在另一个类里面或者者一个方法里面,这样的类称为内部类。

分类:

  • 1.成员内部类:定义在成员位置

成员内部类可以无条件访问外部类的所有成员属性和成员方法(包括private成员和静态成员)。
当成员内部类拥有和外部类同名的成员变量或者者方法时,会发生隐藏现象,即默认情况下访问的是成员内部类的成员。

  • 2.局部内部类

局部内部类是定义在一个方法或者者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者者该作用域内。

  • 3.匿名内部类

匿名内部类就是没有名字的内部类,在公告的时候直接创立对象实例。(由于没有名字)隐含实现一个接口或者继承一个类。

  • 4.静态内部类

指被公告为static的内部类,他可以不依赖内部类而实例,而通常的内部类需要实例化外部类,从而实例化。静态内部类不可以有与外部类有相同的类名。不能访问外部类的普通成员变量,但是可以访问静态成员变量和静态方法(包括私有类型)

内部类的共性:

  • 1.内部类依然是一个独立的类,在编译之后内部类会被编译成独立的.class文件,但是前面冠以外部类的类名和$符号
  • 2.外部类不能直接访问内部类的的成员,但可以通过内部类对象来访问。

作用:

  • 1.每个内部类都能独立的继承一个接口的实现,所以无论外部类能否已经继承了某个(接口的)实现,对于内部类都没有影响。内部类使得多继承的处理方案变得完整,
  • 2.方便将存在肯定逻辑关系的类组织在一起,又可以对外界隐藏。
  • 3.方便编写事件驱动程序
  • 4.方便编写线程代码

6.笼统类和接口区别

  • 语法层面上的区别:

    • 1.笼统类中可以有非笼统方法,接口中都是笼统方法。(java8中新添加default方法,带有具体的实现)
    • 2.笼统类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型的。
    • 3.接口中不能含有静态代码块以及静态方法,而笼统类可以有静态代码块和静态方法;
    • 4.一个类只能继承一个笼统类,而一个类却可以实现多个接口。
  • 设计层面上的区别:

    • 1.笼统类是对一种事物的笼统,即对类笼统,而接口是对行为的笼统。笼统类是对整个类整体进行笼统,包括属性、行为,但是接口却是对类局部(行为)进行笼统。
    • 2.设计层面不同,笼统类作为很多子类的父类,它是一种模板式设计。而接口是一种行为规范,它是一种辐射式设计。

什么是模板式设计?最简单例子,大家都用过ppt里面的模板,假如用模板A设计了ppt B和ppt C,ppt B和ppt C公共的部分就是模板A了,假如它们的公共部分需要改动,则只要要改动模板A即可以了,不需要重新对ppt B和ppt C进行改动。而辐射式设计,比方某个电梯都装了某种报警器,一旦要升级报警器,就必需一律升级。也就是说对于笼统类,假如需要增加新的方法,可以直接在笼统类中增加具体的实现,子类可以不进行变更;而对于接口则不行,假如接口进行了变更,则所有实现这个接口的类都必需进行相应的改动。

意义或者者作用:把少量具备相同属性和方法的组件进行笼统,这样更有利于代码和程序的维护。

7.Serializable 和Parcelable 的区别

Serializable: Java 序列化接口(空接口或者者标记接口) 在硬盘上读写 读写过程中有大量临时变量的生成,Serializable的本质是使用了反射,序列化的过程比较慢
Parcelable Android: 序列化接口 效率高 使用麻烦 在内存中读写(AS有相关插件 一键生成所需方法) 本质是将一个完整的对象进行分解,而分解后的每一部分都是Intent所支持的类型,这样就实现了传递对象的功能了。

假如是仅仅在内存中使用,比方activity、service之间进行对象的传递,强烈推荐使用Parcelable,由于Parcelable比Serializable性能高很多;假如是持久化操作,推荐Serializable,尽管Serializable效率比较低,但是还是要选择它,由于在外界有变化的情况下,Parcelable不能很好的保存数据的持续性。

把对象转换为字节序列的过程称为对象的序列化。

serialVersionUID的详细工作机制是这样的:序列化的时候系统会把当前类的serialVersionUID写入序列化的文件中(也可能是其余中介),当反序列化的时候系统会去检测文件中的serialVersionUID,看它能否和当前类的serialVersionUID一致,假如一致就说明序列化的类的版本和当前类的版本是相同的,这个时候可以成功反序列化;否则就说明当前类和序列化的类相比发生了某些变换,比方成员变量的数量、类型可能会发生变化,这时候就无法正常的反序列化。所以一般来说,我们应该手动去指定serialVersionUID的值,这样即便类发生变化了也能反序列化成功。假如不指定的话,默认根据当前类的结构去生成对应的hash值作为serialVersionUID的值。

8.string转换成integer的方式及原理

//Integer.parseInt(String str)方法public static int parseInt(String s) throws NumberFormatException {        //内部默认调用parseInt(String s, int radix)基数设置为10        return parseInt(s,10);}public static int digit(int codePoint, int radix) {        //基数必需再最大和最小基数之间        if (radix < MIN_RADIX || radix > MAX_RADIX) {            return -1;        }                if (codePoint < 128) {            // Optimized for ASCII            int result = -1;            //字符在0-9字符之间            if ('0' <= codePoint && codePoint <= '9') {                result = codePoint - '0';            }            //字符在a-z之间            else if ('a' <= codePoint && codePoint <= 'z') {                result = 10 + (codePoint - 'a');            }            //字符在A-Z之间            else if ('A' <= codePoint && codePoint <= 'Z') {                result = 10 + (codePoint - 'A');            }            //通过判断result和基数大小,输出对应值            //通过我们parseInt对应的基数值为10,            //所以,只能在第一个判断(字符在0-9字符之间)            //中得到result值 否则后续程序会抛出异常            return result < radix ? result : -1;        }        return digitImpl(codePoint, radix); }

1.parseInt(String s)–内部调用parseInt(s,10)(默认为10进制)
2.正常判断null,进制范围,length等
3.判断第一个字符能否是符号位
4.循环遍历确定每个字符的十进制值(对单个char进行数值计算Character.digit(char ch, int radix) ,该方法返回的肯定是十进制数值)
5.通过*= 和-= 进行计算拼接
6.判断能否为负值 返回结果。

说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 面试题整理-Java部分(一)

发表回复