面试宝典总结:
java 基础
Java 中的八种数据类型
四类 | 八种 | 字节数 | 数据范围 |
---|---|---|---|
整型 | byte | 1 | -128··127 |
整型 | short | 2 | -32768··32767 |
整型 | int | 4 | |
整型 | long | 8 | -2^63 ·· 2^63-1 |
浮点型 | float | 4 | |
浮点型 | double | 8 | |
字符型 | char | 2 | |
布尔型 | boolean | 1 | true,false |
一个byte类型在计算机中占一个字节,那么就是8个bit,就是8位,那么最 大就是11111111,
就是说能表示255个不同的值,那么以0作为中心点,范围就是-128到127
计算机中负数的存储方式是补码。
方法为:对负数的绝对值的二进制值取反,再加一,即为负数的二进制 码。
如:-1的绝对值1的二进制码为00000001,取反得到11111110,再加一为11111111。
而-128的绝对值128的二进制码为10000000,取反得到01111111,再加一为10000000,
正好是Byte的最大表示范围
计算机是如何来区分有无符号数的?
有符号数和无符号数在计算机里表示都是一样的,二进制的补码形式。
是有符号还是无符号,是编译器来辨认的。
例如:
unsigned char uch, char ch;
如何查看一个二进制是否是正数还是负数?
看一个二进制是否是正数还是负数,需要先说明其是存储在计算机中,然后要搞清楚其在计算机中是以有符号进行存储还是无符号进行存储。
- 如果是无符号存储,则其为一个正数。
- 若是有符号存储,则为补码存储。
(补码存储)需要看其最高位,最高位为0,为正数; 反之,为负数。
面向对象的四大特征:
- 继承:继承是从已有类得到继承信息,创建新类的过程。提供继承信息的类叫做父类,得到继承信息的类叫做子类。
- 封装:隐藏一切可隐藏的东西,只向外界提供最简单的编程接口。
- 多态:多态性指允许不同子类型的对象对同一消息做出不同的响应。(同样的对象引用调用同样的方法但是做了不同的事)多态必须满足的两个条件:1. 方法重写,2. 父类引用指向子类
- 抽象:抽象就是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象,抽象只关注对象有哪些属性和行为,不关心具体实现。
访问权限修饰符的区别
修饰符 | 当前类 | 同包 | 子类 | 其他包 |
---|---|---|---|---|
public | √ | √ | √ | √ |
protected | √ | √ | √ | × |
default | √ | √ | × | × |
private | √ | × | × | × |
对象的克隆
克隆出来的对象是一个新的对象。
深拷贝和浅拷贝的区别:
当对象的中的对象是引用数据类型时,浅拷贝指向的时对象的引用,深拷贝才是新的创建。(Clone方法是浅拷贝)
如何实现深拷贝?
如果想深拷贝一个对象(对象中含有引用对象),这个对象非必须要实现Cloneable 接口,实现Clone方法,并且在clone方法内部,把该对象的引用的其他对象也要clone一份,这也意味者这个其他对象内部也要实现Cloneable接口,重写clone方法。
深克隆的方法验证
static class Body implements Cloneable {
public Head head;
public Body() {
}
public Body(Head head) {
this.head = head;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Body newBody = (Body) super.clone();
newBody.head = (Head) head.clone();
return newBody;
}
}
static class Head implements Cloneable {
public Face face;
public Head() {
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public static void main(String[] args) throws CloneNotSupportedException {
Body body = new Body(new Head(new Face()));
Body body1 = (Body) body.clone();
System.out.println("body == body1 : " + (body == body1));
System.out.println("body.head == body1.head : " + (body.head == body1.head));
}
/**
* 程序执行结果:
* body == body1 : false
* body.head == body1.head : false
*/
& 和 && 的区别:
& :
- 位运算符 (可用于基本类型的数据运算(仅限byte,short,int,char,boolean))
- 逻辑与
&&:短路与
虽然& 和 && 运算符都要求左右两端的布尔值都是 true,整个表达式的结果才为true,但是短路与&& 运算符只有当左边的表达式为true时才会继续进行有右边等式的运算,右边为false时,会直接短路掉,右边的表达式不会执行。
java 中如何跳出当前的多重嵌套循环?
break关键字,在最外层循环加一个标记A,通过breakA;可以跳出多重循环
两对象的HashCode值比较。
如果两个对象equals方法返回true,那么它们的 hashCode 值一定相同。
两个对象的 hashCode 值相同,它们的equals方法不一定返回 true。(hash冲突)
A==B判断的两个对象的地址值是否相同,A.equals(B) 比较的是两个对象的值是否相同。
instanceOf 判断的是两个对象是否是同一个类型。
String 是否可以被继承?(final 修饰的类)
由于String 是被final修饰的类,所以不能被继承。
当一个对象被当作参数传递到一个方法后,方法可以改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?
//基础类型作为值传递
public class test {
public static void main(String[] args) {
int i = 1;
System.out.println("before change, i = "+i);
change(i);
System.out.println("after change, i = "+i);
}
public static void change(int i){
i = 5;
}
}
/**
* 输出的结果是 before change i = 1;
* 输出的结果是 after change i = 1;
* 当基本数据类型作为参数传递时,传递的是实参值的副本,即传的是值,无论在函数中怎么操作这个副本,
* 实参的值是不会被改变的。
*/
//对象(引用类型)作为值传递
public class test {
public static void main(String[] args) {
StringBuffer sb = new StringBuffer("Hello ");
System.out.println("before change, sb is "+sb.toString());
change(sb);
System.out.println("after change, sb is "+sb.toString());
}
public static void change(StringBuffer stringBuffer){
stringBuffer.append("world !");
}
}
/**
* before change, sb is Hello
* after change, sb is Hello world !
*/
/**
* 从输出结果中我们可以发现,sb所指向的对象的值被改变了,那么是否我们可以推论出,在Java中,当
* 对象作为参数传递时,传递的是该对象的引用呢?我们再来看下面这个例子
*/
public class test {
public static void main(String[] args) {
StringBuffer sb = new StringBuffer("Hello ");
System.out.println("before change, sb is "+sb.toString());
change(sb);
System.out.println("after change, sb is "+sb.toString());
}
public static void change(StringBuffer stringBuffer){
stringBuffer = new StringBuffer("Hi ");
stringBuffer.append("world !");
}
}
/**
* 如果上面的推论是正确的,即Java中对象作为参数传递,实际传递的是该对象的引用,那么在调用change
* 函数之后,原对象的值应该是会改变的,变为“Hi world !”,但是,当我们运行程序后,结果却是如下所示:
* before change, sb is Hello
* after change, sb is Hello
*
* 原对象的值并没有被改变,这与上面的推论相矛盾!
*/
/**
* 总结就是基本数据类型传递的是形参,形参不影响实参;引用数据类型传递的是地址,形参在方法内被改变,实
* 参也会改变,若在方法内实例化了同名对象,即产生了新的地址,对这个同名对象的改变,由于地址不一样,
* 所以不影响原来的对象
*/
总结:
当基本数据类型作为参数传递时,传递的是实参值的副本,即值的传递
;无论在函数中怎么操作这个副本,实参的值是不会被改变的。
引用数据类型传递的是地址
,形参在方法内被改变,实参也会改变,若在方法内实例化了同名对象
,即产生了新的地址
,对这个同名对象的改变,由于地址不一样,所以不影响原来的对象
方法重载和方法重写的区别
方法的重载和方法的重写都是实现多态的方式。方法的重载是编译时的多态性,而方法的重写是运行时的多态性。
**方法重载的规则**
- 方法名一致
- 请求参数不同(个数不同,参数类型不同)
- 和方法的返回值无关。
- 可以抛出不同的异常,可以有不同的修饰符
**方法的重写**
- 方法名,请求参数,返回值相同
- 构造方法不能被重写,static 和 final 修饰的方法不能被重写
- 重写的方法不能抛出被重写方法更广泛的异常。
抽象类 Abstract 和 interface 的区别?
相同: 都不能被实例化。
不同:
-
抽象类中可以定义构造器,接口中不能定义构造器。
-
接口中的成员全是public 的。抽象类中的成员可以是任何。
-
抽象类中可以有成员变量,接口中没有。
-
抽象类中可以有抽象方法和具体方法。接口中只有抽象方法。
-
有抽象方法的类必须被声明成抽象类,但是抽象类中可以没有抽象方法 ,接口中的成员变量就是常量(接口中的成员变量默认是
public staic final
修饰的)。抽象类中可以有静态方法。 -
抽象类只能够单继承。接口可以多继承。
在 Jdk8 之后,接口中可以有默认方法和静态方法。不仅仅止于抽象方法了。
== 和 equal 的区别
一个是方法一个是运算符, == 如果比较的是基本类型的变量,则比较的是数值是否相等。如果比较的是引用类型,那么比较的是引用类型的地址值是否相等。 equal是用来比较两个对象的内容是否相等。equal是不能用来比较基本类型的(基本数据类型没有equals() 方法,可以做装箱处理然后再使用equals()方法
)。如果对象没有对equal 方法进行重写,那么equal方法比较的是对象的地址值;
String x = "string";
String y = "string";
String z = new String("string");
System.out.println(x==y); // true
System.out.println(x==z); // false
System.out.println(x.equals(y)); // true
System.out.println(x.equals(z)); // true
// new String() 方法是重写开辟了空间,所以 == 的结果为 false
equal 默认情况下是引用的比较(也就是 == 比较),不过很多的类都重写了 equal 方法,比如 String,Integer 等内部将equal 方法变成了 值 得比较。
break 和 continue 的区别
都是用来控制循环语句的。break 是用来结束一个循环的。continue 是用来跳过此次循环,执行下次循环。
throw 和 throws 的区别
throw 是用在方法体中的,用来抛出异常。执行throw 一定是抛出了某种异常。
throws 是用在方法声明后面的,表示可能会跑出某种异常。表示了某种异常的可能性,不一定会抛出异常。
final ,finally,finalize 的区别
final 修饰类,方法,属性,表示为最终态不可变。finally 异常处理语句结构的一部分,表示最终一定会被执行。finalize : Object 的一个方法,在垃圾回收器执行的时候会调用被回收对象的这个方法。更像是一个对象生命周期的临终方法,当此方法被调用代表着该对象的死亡。
final 修饰的类叫最终类,该类不能被继承
final 修饰的方法不能被重写
final 修饰的变量叫常量,常量必须初始化,初始化后不能再被修改
Math.round()方法
数据轴向右取整。 -11.5==》-11 11.5 ==》 12
switch 中可使用的类型
java 5 以前只能使用 byte,short,int ,char 。 java5 开始 引入了 enum 。 java7 后,引入了 String ,但是 Long 类型一直都是不可以的。
String,StringBuffer ,Stringbuild 的区别
String 是被final 修饰的。不能被修改,Stringbuild 的值是可以被修改的。 StringBuffer 是线程安全的类。被 Synchronized 修饰。
String 声明的是不可变的变量,每次操作都会生成新的String对象,而Stringbuild 和 Stringbuffer 都是在原字符串上进行操作的。
StringBuild 是线程不安全的,推荐在单线程下使用,而StringBuffer 是线程安全的推荐在多线程下使用。
public final class StringBuffer extends AbstractStringBuilder implements java.io.Serializable,CharSequence{
public synchronized int length(){return count;}
// StringBuffer 是一个线程安全的类,类被final修饰。类中的所有方法都被 `synchronized`
}
String str="i"与 String str=new String(“i”)一样吗?
不一样,因为内存的分配方式不一样。String str="i"的方式,java 虚拟机会将其分配到常量池中;而 String str=new String(“i”) 则会被分到堆内存中。
BIO,AIO,NIO 有什么区别?
BIO:同步阻塞式IO,简单方便,并发处理效率低
NIO:同步非阻塞IO,客户端和服务器通过channel 信道,实现了多路复用
AIO:NIO的升级,也叫NIO2。异步非阻塞IO
泛型
泛型类型用于类的定义中,被称为泛型类。通过泛型可以完成对一组类的操作对外开放相同的接口。最典型的就是各种容器类,如:List、Set、Map。
public interface List<E> extends Collection<E> {
........
}
//泛型的最基本写法:
classs 类名称<泛型标识:可以随便写任意标识符,标识指定的泛型的类型>{
private 泛型标识 /* (成员变量类型) */ var ;
}
public void showKeyValue1(Generic<?> obj){
Log.d("泛型测试","key value is " + obj.getKey());
}