基础面试题
1、创建一个对象用什么关键字?对象实例与对象引用有何不同?
new关键字,new创建对象实例(对象实例在堆内存中),对象引用指向对象实例(对象引用存放
在栈内存中)。一个对象引用可以指向0个或1个对象 比如:一根绳子可以不系气球,也可以系一个气
球);一个对象可以有n个引用指向它(可以用n条绳子系住一个气球)
2、成员变量与局部变量的区别有哪些
1、 作用域
- 成员变量:针对整个类
- 局部变量:只在某个范围内有效。(一般指的就是方法,语句体
2、存储位置:
- 成员变量:随着对象的创建而存在,随着对象的消失而消失,存储在堆内存
- 局部变量:在方法被调用,或者语句被执行的时候存在,存储在栈内存中。当方法调用完,或者语句结束后,就自动释
3、生命周期
- 成员变量:随着对象的创建而存在,随着对象的消失而消失
- 局部变量:当方法调用完,或者语句结束后,就自动释
4、初始值
- 成员变量:有初始默认值
- 局部变量:没有默认初始值,使用前必须赋值
3、什么是内部类
在java中,可以将一个类的定义放在另一个类的内部就行定义,这就是内部类。内部类本身就是类的一个类的属性,与其他属性定义方式一直
4、内部类的分类有哪些
内部类可以分为四种:成员内部类、局部内部类、匿名内部类和静态内部类。
成员内部类
- 定义在类内部的静态类,就是静态内部类
public class Outer {
private static int radius = 1;
static class StaticInner {
public void visit() {
System.out.println("visit outer static variable:" + radius);
}
}
}
- 静态内部类可以访问外部类所有的静态变量,而不可访问外部类的非静态变量;静态内部类的创建方式,
new 外部类.静态内部类()
,如下:
Outer.StaticInner inner = new Outer.StaticInner();
inner.visit();
成员内部类:
- 定义在类内部,成员位置上的非静态类,就是成员内部类。
public class Outer {
private static int radius = 1;
private int count = 2;
class Inner {
public void visit() {
System.out.println("visit outer static variable:" + radius);
System.out.println("visit outer variable:" + count);
}
}
}
- 成员内部类可以访问外部类的所有变量和方法,包括静态和非静态,私有和公有,成员内部类依赖于外部类的实例,它的创建方式
外部类实例.new 内部类()
,如下:
Outer outer = new Outer()
Outer.Inner inner = outer.new Inner();
inner.visit()
局部内部类
- 定义在方法中的内部类,就是局部内部类
public class Outer {
private int out_a = 1;
private static int STATIC_b = 2;
public void testFunctionClass() {
int inner_c = 3;
class Inner {
private void fun() {
System.out.println(out_a);
System.out.println(STATIC_b);
System.out.println(inner_c);
}
}
Inner inner = new Inner();
inner.fun();
}
public static void testStaticFunctionClass() {
int d = 3;
class Inner {
private void fun() {
// System.out.println(out_a); 编译错误,定义在静态方法中的局部类不可以访问外部类的实例变量
System.out.println(STATIC_b);
System.out.println(d);
}
}
Inner inner = new Inner();
inner.fun();
}
}
- 定义在实例方法中的局部类可以访问外部类的所有变量和方法,定义在静态方法中的局部类只能访
问外部类的静态变量和方法。局部内部类的创建方式,在对应方法内, new 内部类() ,如下:
public static void testStaticFunctionClass(){
class Inner {
}
Inner inner = new Inner()
}
匿名内部类
- 匿名内部类就是没有名字的内部类,日常开发中使用的比较多
public class Outer {
private void test(final int i) {
new Service() {
public void method() {
for (int j = 0; j < i; j++) {
System.out.println("匿名内部类");
}
}
}.method();
}
}
//匿名内部类必须继承或实现一个已有的接口
interface Service{
void method();
}
- 除了没有名字,匿名内部类还有一下特点:
- 匿名内部类必须继承一个抽象类或者实现一个接口。
- 匿名内部类不能定义任何静态成员和静态方。
- 当所有的方法的形参需要被匿名内部类使用时,必须要声明为final。
- 匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法
- 匿名内部类创建方式:
new 类/接口{
//匿名内部类实现部分
}
5、匿名内部类的优点:
1、一个内部类对象可以访问创建它的外部类对象的内容,包括私有数据!
2、内部类不为同一包的其他类所见,具有很好的封装性;
3、内部类有效实现了“多重继承”,优化 java 单继承的缺陷。
4、匿名内部类可以很方便的定义回调
6、局部内部类和匿名内部类访问局部变量的时候,为什么变量必须要加上final?
- 局部内部类和匿名内部类访问局部变量的时候,为什么变量必须要加上final呢?它内部原理是什
么呢?先看这段代码:
public class Outer {
void outMethod() {
final int a = 10;
class Inner {
void innerMethod() {
System.out.println(a);
}
}
}
}
- 以上例子,为什么要加final呢?是因为生命周期不一致, 局部变量直接存储在栈中,当方法执行结束后,非final的局部变量就被销毁。而局部内部类对局部变量的引用依然存在,如果局部内部类要调用局部变量时,就会出错。加了final,可以确保局部内部类使用的变量与外层的局部变量区分开解决了这个
7、== 和 equals 的区别
-
== : 它的作用是判断两个对象的地址是不是相等。即,判断两个对象是不是同一个对象。(基本数
据类型 == 比较的是值,引用数据类型 == 比较的是内存地址) -
equals() : 它的作用也是判断两个对象是否相等。但它一般有两种使用情况:
-
情况1:类没有覆盖 equals() 方法。则通过 equals() 比较该类的两个对象时,等价于通过“==”比较这两个对象
-
情况2:类覆盖了 equals() 方法。一般,我们都覆盖 equals() 方法来两个对象的内容相等;
若它们的内容相等,则返回 true (即,认为这两个对象相等)。 -
String中的equals方法是被重写过的,因为object的equals方法是比较的对象的内存地址,而String的equals方法比较的是对象的
-
当创建String类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,如果有就把它赋给当前引用。如果没有就在常量池中重新创建一个String对象。
-
8、 hashCode 与 equals(重要)
-
HashCode如何检查重复
-
两个对象的 hashCode() 相同,则 equals() 也一定为 true,对吗?
-
hashCode和equals方法的关系
-
面试官可能会问你:“你重写过 hashcode 和 equals 么,为什么重写equals时必须重写hashCode方法?”
hashCode()介绍
-
hashCode()的作用就是获取哈希码,也成为散列码,它实际返回一个int的整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。hashCode() 定义在JDK的Object.java中,这就意味着Java中的任何类都包含有hashCode()函数。
-
散列表存储的是键值对(key-value),它的特点是:能根据“键”快速的检索出对应的“值”。这其中就利用到了散列码!(可以快速找到所需要的对象)
为什么要有 hashCode
我们以“HashSet 如何检查重复”为例子来说明为什么要有 hashCode:
- 当你把对象加入 HashSet 时,HashSet会先计算出对象的hashCode值与已加入的hashCode值进行比较,没有相同的就加入,如果有相同的,然后再去调用equals()方法进行检查,这样我们就大大减少了 equals 的次数,相应就大大提高了执行速度。
hashCode()与equals()的相关规定
- 如果两个对象相等,则hashcode一定也是相同的
- 两个对象相等,对两个对象分别调用equals方法都返回true
- 两个对象有相同的hashcode值,它们也不一定是相等的
因此,equals 方法被覆盖过,则 hashCode 方法也必须被覆盖
hashCode() 的默认行为是对堆上的对象产生独特值。如果没有重写 hashCode(),则该 class 的两个对象无论如何都不会相等(即使这两个对象指向相同的数据
9、当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递
- 是值传递。Java 语言的方法调用只支持参数的值传递。当一个对象实例作为一个参数被传递到方法中时,参数的值就是对该对象的引用。对象的属性可以在被调用过程中被改变,但对对象引用的改变是不会影响到调用者的
10、值传递和引用传递有什么区别
- 值传递:指的是在方法调用时,传递的参数是按值的拷贝传递,传递的是值的拷贝,也就是说传递后就互不相关了。
- 引用传递:指的是在方法调用时,传递的参数是按引用进行传递,其实传递的引用的地址,也就是变量所对应的内存空间的地址。传递的是值的引用,也就是说传递前和传递后都指向同一个引用(也就是同一个内存空间)。