题目来源于:https://www.bilibili.com/read/cv7046891
1.JDK和JRE有什么区别?
- JDK:Java Development Lit,为Java开发工具包,提供包括编译器(javac)、开发工具(javadoc、jre、keytool、jconsole)和更多类库(tools.jar)等,JDK中包含JRE。
- JRE:Java Runtime Environment,为Java程序运行时环境,包含Java虚拟机(JVM)、Java基础类库等。是运行Java编写的程序的必备环境。
一句话解释:JDK用于开发,JRE用于运行,JDK包含JRE。
2. ==和equals的区别是什么?
基本引用类型(byte,short,char,int,long,float,double,boolean):==为值比较。
引用数据类型(class,interface,array):==为内存地址比较。
对于equals来说,equals其实是属于Object超类定义实现的,源代码如下:
// Object超类equals实现
public boolean equals(Object obj) {
return (this == obj);
}
由源代码可得,实际上equals等同于==。
但是,对于String、Integer等包装类来说,它们实在对equals进行过重写,使得equals功能为值比较。源代码如下:
// String类型equals实现
public boolean equals(Object anObject) {
// 如果传入的类型与本类型内存地址一致,则直接返回true
if (this == anObject) {
return true;
}
// 如果传入的类型也为String类型,那么进行字符串值比较
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
// Integer类型equals实现
public boolean equals(Object obj) {
// 如果传入的类型与本类型相同,则返回本类型的值与传入类型的值的对比
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
3.两个对象的hashCode()相同,则equals()也一定为true,对吗?
要解决这个问题,需要理解什么是hash?
维基百科解释为:散列函数(英语:Hash function)又称散列算法、哈希函数,是一种从任何一种数据中创建小的数字“指纹”的方法。散列函数把消息或数据压缩成摘要,使得数据量变小,将数据的格式固定下来。该函数将数据打乱混合,重新创建一个叫做散列值(hash values,hash codes,hash sums,或hashes)的指纹。散列值通常用一个短的随机字母和数字组成的字符串来代表。[1]好的散列函数在输入域中很少出现散列冲突。在散列表和数据处理中,不抑制冲突来区别数据,会使得数据库记录更难找到。
因此可知,hash是可能存在散列冲突的:即不同的两个值,hash后的结果却是一致的。
hashCode为超类Object实现的方法,它是调用本地方法hashCode()实现的。源代码如下:
public native int hashCode();
因此hash冲突在Java的hashCode()实现中也是存在的,因此也存在多种hash冲突的解决方案,如开放地址法、拉链法等。
因此可得结论:hashCode()结果相同,equals的结果不一定为true。
4.final在Java中有什么作用?
在Java中,final可以用来修饰类、函数和变量。final类中的所有方法,会被隐式的指定为final方法。
当其修饰类时,表示该类不可继承。
public final class Person {
}
当其修饰方法时,表示该方法不可被重写。类的private方法会被隐式的指定为final方法。
public class Person {
final void say() {
System.out.println("say...");
}
}
当其修饰变量时,如果该变量类型为基本数据类型,则表示该变量不可被修改;如果该变量为引用数据类型时,则表示该变量引用地址不可被修改。
final String A = "123";
5.Java中的Math.round(-1.5)等于多少?
在Java中,四舍五入的根本实现可以理解为:在原值的基础上+0.5,然后向下取整。源代码如下:
public static int round(float a) {
int intBits = Float.floatToRawIntBits(a);
int biasedExp = (intBits & FloatConsts.EXP_BIT_MASK)
>> (FloatConsts.SIGNIFICAND_WIDTH - 1);
int shift = (FloatConsts.SIGNIFICAND_WIDTH - 2
+ FloatConsts.EXP_BIAS) - biasedExp;
if ((shift & -32) == 0) { // shift >= 0 && shift < 32
// a is a finite number such that pow(2,-32) <= ulp(a) < 1
int r = ((intBits & FloatConsts.SIGNIF_BIT_MASK)
| (FloatConsts.SIGNIF_BIT_MASK + 1));
if (intBits < 0) {
r = -r;
}
// In the comments below each Java expression evaluates to the value
// the corresponding mathematical expression:
// (r) evaluates to a / ulp(a)
// (r >> shift) evaluates to floor(a * 2)
// ((r >> shift) + 1) evaluates to floor((a + 1/2) * 2)
// (((r >> shift) + 1) >> 1) evaluates to floor(a + 1/2)
return ((r >> shift) + 1) >> 1;
} else {
// a is either
// - a finite number with abs(a) < exp(2,FloatConsts.SIGNIFICAND_WIDTH-32) < 1/2
// - a finite number with ulp(a) >= 1 and hence a is a mathematical integer
// - an infinity or NaN
return (int) a;
}
}
public static long round(double a) {
long longBits = Double.doubleToRawLongBits(a);
long biasedExp = (longBits & DoubleConsts.EXP_BIT_MASK)
>> (DoubleConsts.SIGNIFICAND_WIDTH - 1);
long shift = (DoubleConsts.SIGNIFICAND_WIDTH - 2
+ DoubleConsts.EXP_BIAS) - biasedExp;
if ((shift & -64) == 0) { // shift >= 0 && shift < 64
// a is a finite number such that pow(2,-64) <= ulp(a) < 1
long r = ((longBits & DoubleConsts.SIGNIF_BIT_MASK)
| (DoubleConsts.SIGNIF_BIT_MASK + 1));
if (longBits < 0) {
r = -r;
}
// In the comments below each Java expression evaluates to the value
// the corresponding mathematical expression:
// (r) evaluates to a / ulp(a)
// (r >> shift) evaluates to floor(a * 2)
// ((r >> shift) + 1) evaluates to floor((a + 1/2) * 2)
// (((r >> shift) + 1) >> 1) evaluates to floor(a + 1/2)
return ((r >> shift) + 1) >> 1;
} else {
// a is either
// - a finite number with abs(a) < exp(2,DoubleConsts.SIGNIFICAND_WIDTH-64) < 1/2
// - a finite number with ulp(a) >= 1 and hence a is a mathematical integer
// - an infinity or NaN
return (long) a;
}
}
因此,-1.5四舍五入结果为(-1.5+0.5)向下取整为-1。
6.String属于基础的数据类型吗?
Java的数据类型分为基础数据类型和引用数据类型。
其中基础数据类型为8种:
- 整形:byte、short、int、long
- 浮点型:float、double
- 字符型:char
- 布尔型:boolen
因此String不属于基础数据类型,而是引用数据类型。
7.Java中操作字符串都有哪些类型?它们之间有什么区别?
Java中操作字符串都有String、StringBuffer、StringBuilder。
String:使用final修饰,其返回返回都为new String。对String对象的任何更改都会生成新的String对象。
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
...
public String replace(char oldChar, char newChar) {
if (oldChar != newChar) {
int len = value.length;
int i = -1;
char[] val = value; /* avoid getfield opcode */
while (++i < len) {
if (val[i] == oldChar) {
break;
}
}
if (i < len) {
char buf[] = new char[len];
for (int j = 0; j < i; j++) {
buf[j] = val[j];
}
while (i < len) {
char c = val[i];
buf[i] = (c == oldChar) ? newChar : c;
i++;
}
// 返回新的String对象
return new String(buf, true);
}
}
return this;
}
...
}
StringBuffer:大部分方法都使用了synchronized修饰,为线程安全的类,推荐在多线程并发的情况下使用。
public final class StringBuffer
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
...
/**
* @throws IndexOutOfBoundsException {@inheritDoc}
* @since 1.5
*/
@Override
public synchronized StringBuffer append(CharSequence s, int start, int end)
{
toStringCache = null;
super.append(s, start, end);
return this;
}
...
}
StringBuild:类似于StringBuffer,但是方法未使用synchornized进行修饰,为线程不安全的类,但是使用效率上要远高于StringBuffer。
public final class StringBuilder
extends AbstractStringBuilder
implements java.io.Serializable, CharSequence
{
...
@Override
public StringBuilder append(Object obj) {
return append(String.valueOf(obj));
}
@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}
...
}
总结:Java中操作字符串的类型共有三种,分别为String、StringBuffer、StringBuilder。其中String为不可变的,适用于字符串生成不再变化的场景;StringBuffer为线程安全型,适用于需要大量拼接字符串且对线程安全要求较高的场景;StringBuilder为线程不安全类型,适用于需要大量拼接字符串且对线程安全要求较低的场景。
8.String str = "i"与String str = new String("i")一样吗?
java String str = "i"
,相当于将内存地址中的"i"的地址赋值给str变量。如果此时再次定义java String str2 = "i"
,那么str和str2的地址相同。
public static void main(String[] args) {
String str1 = "i";
String str2 = "i";
System.out.println(str1 == str2);
}
而java String str = new String("i")
,实际上是重新创建了对象,并赋值为"i",虽然多次赋值的值相同,但是其内存地址并不相同。
public static void main(String[] args) {
String str1 = new String("i");
String str2 = new String("i");
System.out.println(str1.equals(str2));
System.out.println(str1 == str2);
}
9.如何将字符串反转?
如果此处考察的是Java基础,则可以使用StringBuffer或者StringBuilder进行反转。
public static void main(String[] args) {
String str = "abcdefg";
String res = new StringBuilder(str).reverse().toString();
System.out.println(res);
}
StringBuilder反转实现源码:
// StringBuilder源码,该方法继承自AbstractStringBuilder
@Override
public StringBuilder reverse() {
super.reverse();
return this;
}
// AbstractStringBuilder源码
public AbstractStringBuilder reverse() {
boolean hasSurrogates = false;
int n = count - 1;
for (int j = (n-1) >> 1; j >= 0; j--) {
int k = n - j;
char cj = value[j];
char ck = value[k];
value[j] = ck;
value[k] = cj;
if (Character.isSurrogate(cj) ||
Character.isSurrogate(ck)) {
hasSurrogates = true;
}
}
if (hasSurrogates) {
reverseAllValidSurrogatePairs();
}
return this;
}
如果此处考察的是算法,则可以使用多种方法进行反转。例如:
public static void main(String[] args) {
String str = "abcdefg";
char[] strArr = str.toCharArray();
int i = 0;
int j = strArr.length - 1;
while (i < j) {
char temp = strArr[i];
strArr[i++] = strArr[j];
strArr[j--] = temp;
}
System.out.println(new String(strArr));
}
10.String类的常用方法有哪些?
indexOf():返回指定字符的索引。
charAt():返回指定索引处的字符。
replace():字符串替换。
trim():字符串两端空白去除。
split():将字符串按照指定分隔符分割为字符串数组。
getBytes():返回字符串的byte类型的数据。
length():返回字符串的字符长度。
toLowerCase():将字符串转换为全部小写。
toUpperCase():将字符串转换为全部大写。
substring():截取字符串。
...
11.抽象类必须要有抽象方法吗?
抽象类中的方法不需要必须是抽象法。
但是拥有抽象方法的类必须是抽象类。
public abstract class Person {
protected String name;
protected int age;
public abstract void say();
public void play() {
System.out.println("play...");
}
}
12.普通类和抽象类有哪些区别?
抽象类和普通类是相对的概念。通常抽象类是由一系列普通类的共性抽象而成,例如:男人、女人的普通类,可以抽象成人类的类。
- 抽象类必须使用abstract关键字修饰,而普通类不可以使用该关键字修饰。
- 普通类可以实例化,抽象类不可以实例化。
- 抽象类方法必须为public或者protected,而普通类还可以使用private(private修饰的方法不可以被继承)。
- 一个普通类若继承自抽象类,则必须实现抽象类中的抽象方法必须。如果子类没有实现父抽象类中抽象方法,那么该子类必须也是抽象类。
13.抽象类能使用final修饰吗?
被final关键字修饰的类不可被继承,因此抽象类不能使用final关键字进行修饰。
14.接口和抽象类有什么区别?
- 从概念上来说,抽象类是对事物本质的抽象,例如男人、女人和人类;而接口是对动作的抽象,例如说话、吃饭等。
- 从设计层面来说,抽象类一种模板设计,而接口是一种行为规范。如果抽象类中新增了方法,子类是无需进行更新的;但是接口内新增了方法,所有实现该接口的类必须进行更新。
- 从语法层面上来说
- 抽象类可以提供成员方法的实现细节,而接口中只能存在
java public abstract
方法。 - 抽象类中的成员变量可以是各种类型的,但是接口中的成员变量只能是
java public static final
类型的。 - 抽象类中可以包含静态方法和静态代码块,而接口中不能含有静态方法和静态代码块。
- 一个类只能继承一个抽象类,但是可以实现多个接口。
- 抽象类可以提供成员方法的实现细节,而接口中只能存在
15.Java中的IO流分为几种?
- 按照流的流向分:可以分为输入流和输出流。
- 按照流的划分单元分:可以分为字节流和字符流。
- 字节流处理的最基本单位1byte,通常用来处理二进制数据。
- 字符流处理的最基本单位为2byte的Unicode,通常用来处理文本数据。
- 按照流的角色划分为节点流和处理流。
Java IO.png
图片来源于:https://blog.csdn.net/qq_35771266/article/details/94850047
16.BIO、NIO、AIO有什么区别?
- BIO(Blocking IO),同步阻塞IO。
- NIO(Non-Blocking IO),同步非阻塞IO。
- AIO(Aysnc IO),异步非阻塞IO。
17.Files的常用方法有哪些?
Files.exists():检测文件路径是否存在。
Files.createFile():创建文件。
Files.createDirectory():创建目录。
Files.delete():删除文件或者目录。
Files.copy():复制文件。
Files.move():移动文件。
Files.size():查看文件数量。
Files.read():读取文件。
Files.write():写入文件。
...
网友评论