1 Java面向对象的特征
1.抽象:
抽象是指从特定的角度出发,从已经存在的一些事物中抽取我们所关注的特性、行为,从而形成一个新的事物的思维过程,是一种从复杂到简洁的思维方式。
一是过程抽象,二是数据抽象。
2.继承:
继承是面向对象实现软件复用的重要手段,当子类继承父类后,子类是一种特殊的父类,能直接或间接获得父类里的成员。
继承的缺点:1)继承是一种强耦合关系,父类变子类也必须变;2)继承破坏了封装,对于父类而言,它的实现细节对子类来说都是透明的。
3.封装:
封装是指将对象的实现细节隐藏起来,然后通过公共的方法来向外暴露出该对象的功能。
使用封装不仅仅安全,更可以简化操作。
4.多态性:
多态简而言之就是同一个行为具有多个不同表现形式或形态的能力。
多态的条件:1)继承;2)重写;3)向上转型。
2 重载与重写的区别
答:覆盖(Override)是指子类对父类方法的一种重写,只能比父类抛出更少的异常,访问权限不能比父类的小,被覆盖的方法不能是 private 的,否则只是在子类中重新定义了一个新方法。
重载(Overload)表示同一个类中可以有多个名称相同的方法,但这些方法的参数列表各不相同。
面试官: 那么构成重载的条件有哪些?
答:参数类型不同、参数个数不同、参数顺序不同。
面试官: 函数的返回值不同可以构成重载吗?为什么?
答:不可以,因为 Java 中调用函数并不需要强制赋值。举例如下:
如下两个方法:
void f(){}
int f(){ return 1; }
只要编译器可以根据语境明确判断出语义,比如在 int x = f(); 中,那么的确可以据此区分重载方法。不过, 有时你并不关心方法的返回值,你想要的是方法调用的其他效果 (这常被称为 “为了副作用而调用” ),这时你可能会调用方法而忽略其返回值,所以如果像下面的调用:
f();
此时 Java 如何才能判断调用的是哪一个 f() 呢?别人如何理解这种代码呢?所以,根据方法返回值来区分重载方法是行不通的。
3 抽象类和接口的区别有哪些?
1 抽象类中可以没有抽象方法;接口中的方法必须是抽象方法;
2 抽象类中可以有普通的成员变量;接口中的变量必须是 static final 类型的,必须被初 始化,接口中只有常量,没有变量。
3 抽象类只能单继承,接口可以继承多个父接口;
4 Java 8 中接口中会有 default 方法,即方法可以被实现。
面试官:抽象类和接口如何选择?
1 如果要创建不带任何方法定义和成员变量的基类,那么就应该选择接口而不是抽象类。
2 由于Java中只能单继承,多实现,所以如果子类已经有父类了,然后需要一些方法,此 时需要创建接口
4 static 的概念
“static” 关键字表明一个成员变量或者是成员方法可以在没有所属的类的实例变量的情况下被访问。
面试官:Java中是否可以覆盖(override)一个 private 或者是 static 的方法?
答:Java 中 static 方法不能被覆盖,因为方法覆盖是基于运行时动态绑定的,而 static 方法是编译时静态绑定的。static 方法跟类的任何实例都不相关,所以概念上不适用。
Java 中也不可以覆盖 private 的方法,因为 private 修饰的变量和方法只能在当前类中使用,如果是其他的类继承当前类是不能访问到 private 变量或方法的,当然也不能覆盖。
5 值传递 or 引用传递
public class Test1 {
public static void main(String[] args) {
int m=1;
int n=2;
swap(m,n);
System.out.println("m="+m+",n="+n);//输出m=1,n=2
}
private static void swap(int m, int n) {
// TODO Auto-generated method stub
int temp=m;
m=n;
n=temp;
}
}
为什么会这样?
因为 Java 是值传递的,也就是说,我们在调用一个需要传递参数的函数时,传递给函数的参数并不是我们传递进去的参数本身,而是它的一个副本,我们改变了数据其实只是改变了副本的数据而已,并不会对原来的参数有任何的改变。
public static void main(String[] args) {
Person p=new Person();
p.setAge(10);
changeAge(p);
System.out.println(p.getAge());
}
private static void changeAge(Person person) {
// TODO Auto-generated method stub
person.setAge(20);//输出为20
}
又是为什么呢?
可以理解为主函数将 person 复制了一份到 changeAge 函数中去,最终还是只改变了 changeAge 中复制的那一份参数的值,而原本的参数并没有改变,但 changeAge 中的那一份和原本的参数指向了同一个内存区域
6 Int 和 Integer 有什么区别,如何理解自动拆箱与自动装箱
Integer i=5;
Integer k=5;
System.out.println(i==k);//true
Integer m=500;
Integer n=500;
System.out.println(m==n);//false
Integer h=new Integer(5);
Integer g=new Integer(5);
System.out.println(h==g);//false
原因 :
第一个返回true很好理解,就像上面讲的,a和b指向相同的地址。
第二个返回false是为什么呢?这是因为 Integer 有缓存机制,在 JVM 启动初期就缓存了 -128 到 127 这个区间内的所有数字。
第三个返回false是因为用了new关键字来开辟了新的空间,i和j两个对象分别指向堆区中的两块内存空间。
int是基本数据类型,而Integer是封装类 ,自动装箱就是基本数据类型到对象数据类型,拆箱就是对象数据类型到基本数据类型
7 String 常见问题
理解什么是栈,方法区,以及堆。
栈中保存的一般都为 一个基本数据类型 或者一个对象的引用
堆里面保存的一般为new关键字和构造器创建的对象
静态区 一般常量 常量池等;
String str1 = “ABCD”;最多创建一个String对象,最少不创建String对象.如果常量池中,存在”ABCD”,那么str1直接引用,此时不创建String对象.否则,先在常量池先创建”ABCD”内存空间,再引用.
String str2 = new String(“ABCD”);最多创建两个String对象,至少创建一个String对象。new关键字绝对会在堆空间创建一块新的内存区域,所以至少创建一个String对象。
String 和StringBuffer的区别StringBuilder
String对象不可变,也就是说当对象创建完毕之后,该对象的内容(字符序列)是不允许改变的,如果内容改变则会创建一个新的String对象,返回到原地址中。
StringBuilder/StringBuffer:当对象创建完毕之后,该对象的内容可以发生改变,当内容发生改变的时候,对象保持不变。
单独使用"“引号创建的字符串都是直接量,编译期就已经确定存储到常量池中;
使用new String(”")创建的对象会存储到堆内存中,是运行期才创建;
使用只包含直接量的字符串连接符如"aa" + “bb"创建的也是直接量编译期就能确定,已经确定存储到常量池中(str2和str3);
使用包含String直接量(无final修饰符)的字符串表达式(如"aa” + s1)创建的对象是运行期才创建的,存储在堆中;
通过变量/调用方法去连接字符串,都只能在运行时期才能确定变量的值和方法的返回值,不存在编译优化操作.
String常用方法
8 Java 对象初始化顺序?
public class Derive extends Base
{
private Member m1 = new Member("Member 1");
{
System.out.println("Initial Block()");
}
public Derive() {
System.out.println("Derive()");
}
private Member m2 = new Member("Member 2");
private int i = getInt();
private int getInt()
{
System.out.println("getInt()");
return 2;
}
public static void main(String[] args)
{
new Derive();
}
}
class Base
{
public Base()
{
System.out.println("Base()");
}
}
class Member
{
public Member(String m)
{
System.out.println("Member() "+m);
}
}//程序的输出结果是:
Base()
Member() Member 1
Initial Block()
Member() Member 2
getInt()
Derive()
答:不考虑静态成员的初始化,调用一个对象的构造函数时,程序先调用父类的构造函数(可以通过super关键字指定父类的构造函数,否则默认调用无参的构造函数,并且需要在子类的构造函数的第一行调用),之后静态成员变量的初始化函数和静态初始化块则按照在代码当中的顺序执行,成员变量如果没有指定值的话则赋予默认值,即基本数据类型为0或false等,对象则为null;最后调用自身构造函数。
9 exception 和 error 有什么区别?
答:exception 和 error都是 Throwable 的子类。exception 用于用户程序可以捕获的异常情况;error 定义了不期望被用户程序捕获的异常。
exception 表示一种设计或设计的问题,也就是说只要程序正常运行,从不会发生的情况;而 error 表示回复不是不可能但是很困难的情况下的一种严重问题,比如内存溢出,不可能指望程序处理这样的情况。
10 throw 和 throws 有什么区别?
throw 关键字用来在程序中明确的抛出异常,相反,throws 语句用来表明方法不能处理的异常。每一个方法都必须要指定哪些异常不能处理,所以方法的调用者才能够确保处理可能发生的异常,多个异常是用逗号分隔的。