【面试】基本数据类型+String相关-这一篇全了解

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/w372426096/article/details/82857144

16、String类能不能被继承,为什么?这种设计有什么好处?

解:

String是final类型,final类不能被继承。

Java之所以被设计成final类是有一定的考虑在的,主要在以下几个方面。

缓存Hashcode

Java中经常会用到字符串的哈希码(hashcode)。例如,在HashMap中,字符串的不可变能保证其hashcode永远保持一致,这样就可以避免一些不必要的麻烦。这也就意味着每次在使用一个字符串的hashcode的时候不用重新计算一次,这样更加高效。

使其他类的使用更加便利

在介绍这个内容之前,先看以下代码:

HashSet<String> set = new HashSet<String>();

set.add(new String("a"));

set.add(new String("b"));

set.add(new String("c"));

for(String a: set)

     a.value = "a";

在上面的例子中,如果字符串可以被改变,那么以上用法将有可能违反Set的设计原则,因为Set要求其中的元素不可以重复。上面的代码只是为了简单说明该问题,其实上面的代码也无法编译通过,String的value字段并无发从外部访问。

安全性

String被广泛的使用在其他Java类中充当参数。比如网络连接、打开文件等操作。如果字符串可变,那么类似操作可能导致安全问题。因为某个方法在调用连接操作的时候,他认为会连接到某台机器,但是实际上并没有(其他引用同一String对象的值修改会导致该连接中的字符串内容被修改)。可变的字符串也可能导致反射的安全问题,因为他的参数也是字符串。

不可变对象天生就是线程安全的

因为不可变对象不能被改变,所以他们可以自由地在多个线程之间共享。不需要任何同步处理。总之,String被设计成不可变的主要目的是为了安全和高效。所以,使String是一个不可变类是一个很好的设计。

17:待编辑

Java中有8种基本数据类型

分为三大类。

字符型:char(16位)

布尔型:boolean

数值型:1.整型:byte(8位)、short(16位)、int(32位)、long(64位) 2.浮点型:float(32位)、double(64位)

String不是基本数据类型,是引用类型。

19、整型的几种中,各个类型的取值范围是多少,如何计算的?超出范围会发生什么?

解:

Java中的整型主要保函byte、short、int和long这四种,表示的数字范围也是从小到大的,之所以表示范围不同主要和他们存储数据时所占的字节数有关。

先来个简答的科普,1字节=8位(bit)。java中的整型属于有符号数。

先来看计算中8bit可以表示的数字:最小值:10000000 (-128)(-2^7)最大值:01111111(127)(2^7-1)具体计算方式参考:链接:Java中,为什么byte类型的取值范围为-128~127? - CSDN博客

整型的这几个类型中,  

byte:byte用1个字节来存储,范围为-128(-2^7)到127(2^7-1),在变量初始化的时候,byte类型的默认值为0。

short:short用2个字节存储,范围为-32,768 (-2^15)到32,767 (2^15-1),在变量初始化的时候,short类型的默认值为0,一般情况下,因为Java本身转型的原因,可以直接写为0。  

int:int用4个字节存储,范围为-2,147,483,648 (-2^31)到2,147,483,647 (2^31-1),在变量初始化的时候,int类型的默认值为0。  

long:long用8个字节存储,范围为-9,223,372,036,854,775,808 (-2^63)到9,223,372,036, 854,775,807 (2^63-1),在变量初始化的时候,long类型的默认值为0L或0l,也可直接写为0。

上面说过了,整型中,每个类型都有一定的表示范围,但是,在程序中有些计算会导致超出表示范围,即溢出。如以下代码:

int i = Integer.MAX_VALUE;

int j = Integer.MAX_VALUE;

int k = i + j;

System.out.println("i (" + i + ") + j (" + j + ") = k (" + k + ")");

输出结果:i (2147483647) + j (2147483647) = k (-2)

这就是发生了溢出,溢出的时候并不会抛异常,也没有任何提示。所以,在程序中,使用同类型的数据进行运算的时候,一定要注意数据溢出的问题。

20、什么是浮点型。什么是单精度和双精度。为什么代码中不要用浮点数表示金额。

解:

在计算机科学中,浮点是一种对于实数的近似值数值表现法,由一个有效数字(即尾数)加上幂数来表示,通常是乘以某个基数的整数次指数得到。以这种表示法表示的数值,称为浮点数(floating-point number)。

计算机使用浮点数运算的主因,在于电脑使用二进位制的运算。例如:4÷2=2,4的二进制表示为100、2的二进制表示为010,在二进制中,相当于退一位数(100 -> 010)。

1的二进制是01,1.0/2=0.5,那么,0.5的二进制表示应该为(0.1),以此类推,0.25的二进制表示为0.01,所以,并不是说所有的十进制小数都能准确的用二进制表示出来,如0.1,因此只能使用近似值的方式表达。

也就是说,十进制的小数在计算机中是由一个整数或定点数(即尾数)乘以某个基数(计算机中通常是2)的整数次幂得到的,这种表示方法类似于基数为10的科学计数法。

一个浮点数a由两个数m和e来表示:a = m × be。在任意一个这样的系统中,我们选择一个基数b(记数系统的基)和精度p(即使用多少位来存储)。m(即尾数)是形如±d.ddd...ddd的p位数(每一位是一个介于0到b-1之间的整数,包括0和b-1)。如果m的第一位是非0整数,m称作正规化的。有一些描述使用一个单独的符号位(s 代表+或者-)来表示正负,这样m必须是正的。e是指数。

位(bit)是衡量浮点数所需存储空间的单位,通常为32位或64位,分别被叫作单精度和双精度。

单精度浮点数在计算机存储器中占用4个字节(32 bits),利用“浮点”(浮动小数点)的方法,可以表示一个范围很大的数值。

比起单精度浮点数,双精度浮点数(double)使用 64 位(8字节) 来存储一个浮点数。

由于前面说过,计算机中保存的小数其实是十进制的小数的近似值,并不是准确值,所以,千万不要在代码中使用浮点数来表示金额等重要的指标。建议使用BigDecimal或者Long(单位为分)来表示金额。

21、Java中的char是否可以存储中文字符?

解:

Java中的char存储占2个字节。中文使用unicode编码,同样占两个字节。只要在unicode编码中有的中文,即可以用char存储。除了一下生僻字和特殊字之外。

22、int和Integer,boolean和Boolean等,之间有什么区别。

解:

1. 默认值不同,基本类型的默认值为0, false或\u0000,包装类默认为null

2. 初始化不同,一个需要new,一个不需要

3. 存储方式不同

4. int.class是原始类型,Integer.class是对象类型, 所以一个有成员变量和方法,一个没有。包装类就是把基本类型 「包装」 在一个类里,并提供一些常用的操作。毕竟面向对象。

23、拆箱装箱

https://blog.csdn.net/w372426096/article/details/81909792

24、在接口定义的时候,要定义一个字段表示是否成功,你会选以下哪种方式?为什么?

boolean success

Boolean success

boolean isSuccess

Boolean isSuccess

解:

建议使用第一种

首先,作为接口的返回对象的参数,这个字段不应该有不确定的null值,而Boolean类型的默认值是null,而boolean的默认值是false,所以,建议使用boolean来定义参数。其他,关于参数名称,要使用success还是isSuccess,这一点在阿里巴巴Java开发手册中有明确规定和解释:

【强制】 POJO 类中的任何布尔类型的变量,都不要加 is,否则部分框架解析会引起序列化错误。

反例: 定义为基本数据类型 boolean isSuccess;的属性,它的方法也是 isSuccess(), RPC框架在反向解析的时候, “ 以为” 对应的属性名称是 success,导致属性获取不到,进而抛出异常。

25、String s = new String("hollis"); 定义了几个对象。

解:

字符串的分配,和其他的对象分配一样,耗费高昂的时间与空间代价。JVM为了提高性能和减少内存开销,在实例化字符串常量的时候进行了一些优化。为了减少在JVM中创建的字符串的数量,字符串类维护了一个字符串池,每当代码创建字符串常量时,JVM会首先检查字符串常量池。如果字符串已经存在池中,就返回池中的实例引用。如果字符串不在池中,就会实例化一个字符串并放到池中。Java能够进行这样的优化是因为字符串是不可变的,可以不用担心数据冲突进行共享。

String s = new String("hollis"); 创建多少个对象?

在常量池中查找是否有“hollis”对象

有则返回对应的引用实例

没有则创建对应的实例对象

在堆中 new 一个 String("hollis") 对象

将对象地址赋值给s,创建一个引用

所以,常量池中没有“hollis”字面量则创建两个对象,否则创建一个对象,以及创建一个引用。

26、如何比较两个字符串?

解:对于字符串的比较,一般目的是比较字符串内容是否相等,这种情况下,要使用equals()方法来比较,而不是使用'=='。 equals()比较的是内容是否相同,'=='比较的是地址是否相同。

27、String有没有长度限制,为什么?如果有,超过限制会发生什么?

解:

编译期

首先,我们先来合理的推断一下,当我们在代码中使用String s = "";的形式来定义String对象的时候,""中字符的个数有没有限制呢?

既然是合理的推断,那就要要足够的依据,所以我们可以从String的源码入手,根据public String(char value[], int offset, int count)的定义,count是int类型的,所以,char value[]中最多可以保存Integer.MAX_VALUE个,即2147483647字符。(jdk1.8.0_73)

但是,实验证明,String s = "";中,最多可以有65534个字符。如果超过这个个数。就会在编译期报错。

public static void main(String[] args) {

String s = "a...a";// 共65534个a

System.out.println(s.length());

String s1 = "a...a";// 共65535个a

System.out.println(s1.length());

}

以上代码,会在String s1 = "a...a";// 共65535个a处编译失败:

✗ javac StringLenghDemo.java

StringLenghDemo.java:11: 错误: 常量字符串过长

明明说好的长度限制是2147483647,为什么65535个字符就无法编译了呢?

当我们使用字符串字面量直接定义String的时候,是会把字符串在常量池中存储一份的。那么上面提到的65534其实是常量池的限制。

常量池中的每一种数据项也有自己的类型。Java中的UTF-8编码的Unicode字符串在常量池中以CONSTANT_Utf8类型表示。

CONSTANTUtf8info是一个CONSTANTUtf8类型的常量池数据项,它存储的是一个常量字符串。常量池中的所有字面量几乎都是通过CONSTANTUtf8info描述的。CONSTANTUtf8_info的定义如下:

CONSTANT_Utf8_info {

u1 tag;

u2 length;

u1 bytes[length];

}

由于本文的重点并不是CONSTANTUtf8info的介绍,这里就不详细展开了,我们只需要我们使用字面量定义的字符串在class文件中,是使用CONSTANTUtf8info存储的,而CONSTANTUtf8info中有u2 length;表明了该类型存储数据的长度。

u2是无符号的16位整数,因此理论上允许的的最大长度是2^16=65536。而 java class 文件是使用一种变体UTF-8格式来存放字符的,null 值使用两个 字节来表示,因此只剩下 65536- 2 = 65534个字节。

关于这一点,在the class file format spec中也有明确说明:

The length of field and method names, field and method descriptors, and other constant string values is limited to 65535 characters by the 16-bit unsigned length item of the CONSTANTUtf8info structure (§4.4.7). Note that the limit is on the number of bytes in the encoding and not on the number of encoded characters. UTF-8 encodes some characters using two or three bytes. Thus, strings incorporating multibyte characters are further constrained.

也就是说,在Java中,所有需要保存在常量池中的数据,长度最大不能超过65535,这当然也包括字符串的定义咯。

运行期

上面提到的这种String长度的限制是编译期的限制,也就是使用String s= "";这种字面值方式定义的时候才会有的限制。

那么。String在运行期有没有限制呢,答案是有的,就是我们前文提到的那个Integer.MAX_VALUE ,这个值约等于4G,在运行期,如果String的长度超过这个范围,就可能会抛出异常。(在jdk 1.9之前)

int 是一个 32 位变量类型,取正数部分来算的话,他们最长可以有

2^31-1 =2147483647 个 16-bit Unicodecharacter

2147483647 * 16 = 34359738352 位

34359738352 / 8 = 4294967294 (Byte)

4294967294 / 1024 = 4194303.998046875 (KB)

4194303.998046875 / 1024 = 4095.9999980926513671875 (MB)

4095.9999980926513671875 / 1024 = 3.99999999813735485076904296875 (GB)

有近 4G 的容量。

28、String的“+”是如何实现的?

解:

1. String s = "a" + "b",编译器会进行常量折叠(因为两个都是编译期常量,编译期可知),即变成 String s = "ab"

2. 对于能够进行优化的(String s = "a" + 变量 等)用 StringBuilder 的 append() 方法替代,最后调用 toString() 方法 (底层就是一个 new String())

29、String,StringBuilder和StingBuffer之间的区别与联系。

解:

String是字符串常量,StringBuffer是字符串变量(线程安全),StringBuilder是字符串变量(非线程安全)。

简要说,String类型和StringBuffer类型主要性能区别其实在于String是不可变对象,每次对String类型进行改变其实都等同于生成了一个新的String对象,然后将指针指向新的String对象,所以经常改变内容的字符串最好不用String,而如果是StringBuffer,每次结果都会对StringBuffer对象本身进行操作,而不是新的对象,所以一般情况下推荐使用StringBuffer.

StringBuffer

java.lang.StringBuffer线程安全的可变字符序列。一个类似于String的字符串缓冲区,可将字符缓冲区安全地用于多个线程。

StringBuffer上主要操作是append和insert方法,可重载这些方法以接受任意类型的数据。

StringBulider

java.lang.StringBuilder 提供一个与StringBuffer兼容的API,但不保证同步,该类被设计用于StringBuffer的一个简易替换,用于字符串缓冲区被单个线程使用的时候。如果可能,建议优先使用。

大多数实现中,它比StringBuffer要快。

30、substring()方法到底做了什么?不同版本的JDK中是否有区别?为什么?

解:

参考:链接:三张图彻底了解JDK 6和JDK 7中substring的原理及区别-HollisChuang's ...

31、如何理解String的intern方法,不同版本JDK有何不同?为什么?

解:

intern()是String类中的一个public方法,用法如下:

String s1 = new String("Hollis");

String s2=s2.intern();

System.out.println( s1==s2 );

输出结果为 true。

当一个String实例str调用intern()方法时,Java查找常量池中是否有相同Unicode的字符串常量,如果有,则返回其的引用,如果没有,则在常量池中增加一个Unicode等于str的字符串并返回它的引用;

不同版本的JDK中有何不同?

先看下以下代码:

String str1 = new StringBuilder("Hollis").append("Chuang").toString();

System.out.println(str1.intern() == str1);

打印结果:

jdk6 下false

jdk7 下true

Java 6 和Java7 中intern的表现有所不同,导致不同的原因是因为在Java 7中常量池的位置从PermGen区改到了Java堆区中。

jdk1.6中 intern 方法会把首次遇到的字符串实例复制到永久待(常量池)中,并返回此引用;但在jdk1.7中,只是会把首次遇到的字符串实例的引用添加到常量池中(没有复制),并返回此引用。

对于以上代码中的str1.intern() ,在jdk1.6中,会把“HollisChuang”这个字符串复制到常量池中,并返回他的引用。所以str2.intern()的值和str2的值,即两个对象的地址是不一样的。

对于以上代码中的str1.intern() ,在jdk1.7中,会把str2的引用保存到常量池中,并把这个引用返回。所以str2.intern()的值和str2的值是相等的。

扩展另外一个例子:

String str2 = new StringBuilder("ja").append("va").toString();

System.out.println(str2.intern() == str2);以上代码,无论在1.6还是1.7中都会返回false。原因是"ja" + "va" = "java",这个常量在常量池中已经有了,因为这个字符串在Java中随处可见,曾经被初始化过。所以,在1.7中str2.intern()返回的内容是很久之前初始化的时候的那个引用,自然和刚刚创建的字符串的应用不相等。

32、Java中整型的缓存机制

https://blog.csdn.net/w372426096/article/details/80660851

33、在Java的代码中以及数据库存储中,如何对金额进行表示和计算。

解:

一般有两种方案。

1.以元为单位。Java中存储类型为BigDecimal,数据库中存储类型为number(10,2)。计算过程中保留两位小数,可考虑四舍五入或者向上或者向下取整。根据业务实际情况决定。

2.以分为单位。Java中存储类型为Long,数据库中存储类型为big int。计算过程中保留整数,考虑四舍五入或者取整。

34、打包分类

https://blog.csdn.net/w372426096/article/details/80661177

35、String限制(私密)

https://blog.csdn.net/w372426096/article/details/80661218

参考:球友

猜你喜欢

转载自blog.csdn.net/w372426096/article/details/82857144
今日推荐