Java基础知识总结(四)

此篇博文是总结一些Java面试题,以张孝祥老师的《Java面试宝典》为主

 

1、&与&&的区别

  1)&和&&都可以用作逻辑与的运算符,表示逻辑与(and),当运算符两边的表达式的结果都为true时,整个运算结果才为true,否则,只要有一方为false,则结果为false。

  2)&&还具有短路的功能,即如果第一个表达式为false,则不再计算第二个表达式,例如,对于if(str!=null&&!str.equals(“”))表达式,当str为null时,后面的表达式不会执行,所以不会出现NullPointerException如果将&&改为&,则会抛出NullPointerException异常。If(x==33&++y>0)y会增长,If(x==33&&++y>0)不会增长

  3)&还可以用作位运算符,当&操作符两边的表达式不是boolean类型时,&表示按位与操作,我们通常使用0x0f来与一个整数进行&运算,来获取该整数的最低4个bit位,例如,0x31&0x0f的结果为0x01。

 

2、在JAVA中如何跳出当前的多重嵌套循环?

  1)在Java中,要想跳出多重循环,可以在外面的循环语句前定义一个标号,然后在里层循环体的代码中使用带有标号的break语句,即可跳出外层循环。例如:

1 ok:
2 for(int i=0;i<10;i++){
3   for(int j=0;j<10;j++){
4        System.out.println("i="+i+",j="+j);
5        if(j==5)
6           break ok;
7     }
8 }

  2)我个人通常并不使用标号这种方式,而是让外层的循环条件表达式的结果可以受到里层循环体代码的控制,例如,要在二维数组中查找到某个数字。

 1 public class Test {
 2     public static void main(String[] args) {
 3         int arr[][]={{1,2,3},{4,5,6,7},{9}};
 4         boolean found=false;
 5         for(int i=0;i<arr.length&&!found;i++){
 6             for(int j=0;j<arr[i].length;j++){
 7                 System.out.println("i="+i+",j="+j);
 8                 if(arr[i][j]==5){
 9                     found=true;
10                     break;
11                 }
12             }
13         }
14     }
15 }

 

3、switch语句能否作用在byte上,能否作用在long上,能否作用在String上?

  在switch(expr1)中,expr1只能是一个整数表达式或者枚举常量(更大字体),整数表达式可以是int基本类型或Integer包装类型,由于,byte,short,char都可以隐含转换为int,所以,这些类型以及这些类型的包装类型也是可以的。显然,long和String类型都不符合switch的语法规定,并且不能被隐式转换成int类型,所以,它们不能作用于swtich语句中。

 

4、用最有效率的方法算出2乘以8等於几?

  2<<3,因为将一个数左移n位,就相当于乘以了2的n次方,那么,一个数乘以8只要将其左移3位即可,而位运算cpu直接支持的,效率最高,所以,2乘以8等於几的最效率的方法是2<<3。

 

5、使用final关键字修饰一个变量时,是引用不能变,还是引用的对象不能变?

  使用final关键字修饰一个变量时,是指引用变量不能变,引用变量所指向的对象中的内容还是可以改变的。

  例如,对于如下语句:final StringBuffer a=new StringBuffer("immutable");执行如下语句将报告编译期错误:a=new StringBuffer("");但是,执行如下语句则可以通过编译:a.append("broken!");有人在定义方法的参数时,可能想采用如下形式来阻止方法内部修改传进来的参数对象:public void method(final StringBuffer param){}实际上,这是办不到的,在该方法内部仍然可以增加如下代码来修改参数对象:param.append("a");

1 public class Test {
2     public static void main(String[] args) {
3         final StringBuffer a=new StringBuffer("Hello");
4         //a=new StringBuffer("hello");//编译错误
5         a.append(",world");
6         System.out.println(a.toString());//Hello,world
7     }
8 }

 

6、“==”和equals方法的区别

  1)“==”操作符是用来比较连个变量的值是否相等,也就是用来比较变量所对应的内存中所存储的数值是否相同,要比较两个基本类型的数据或两个引用变量是否相等,只能用==操作符。

  2)equals方法是用来比较两个独立对象的内容是否相等。

  3)如果一个类没有自己定义equals方法,那么它将继承Object类的equals方法,Object类的equals方法的实现代码如下:

1 public boolean equals(Object obj) {
2     return (this == obj);
3 }

  从代码中可以得出以下结论:如果一个类没有自己定义equals方法,它默认的equals方法(从Object类继承的)就是使用==操作符,也是在比较两个变量指向的对象是否是同一个对象,这时候使用equals方法和使用==操作符会得到同样的结果,如果比较的是两个独立的对象则总返回false。如果你编写的类希望能够比较该类创建的两个实例对象的内容是否相等,那么你必须覆盖equals方法,由你编写的代码来决定在什么情况即可认为两个对象的内容是相同的。

 

7 、Integer和int的区别

  1)int是java提供的8中原始数据类型之一,java为每个呀un是数据类型提供了封装类,Integer是java为int提供的封装类(封装类=数据+操作)。

  2)int的默认值为0,而Integer的默认值为null,即Integer可以区分出为赋值和值为0的区别,int无法表达出为赋值的情况。例如:想要表达出没有参加考试和考试成绩为0的区别,只能使用Integer。在JSP中Integer的默认值为null,所以用EL表达式在文本框中显示时,值为空表字符串,而int默认值为0,所以用EL表达式在文本框中显示时,结果为0,所以,int不适合作为WEB层的表单数据的类型。

  3)Integer提供了一系列与int相关的操作方法。

 

8、Math.round、Math.float、Math.ceil

  Math类提供了三个与取整有关的方法:ceil、floor、round。

  ceil:向上取整。如:Math.ceil(11.5),结果为:12;Math.ceil(-11.5),结果为:-11。

  floor:向下取整。如:Math.floor(11.5),结果为:11;Math.floor(-11.5),结果为:-12。

  round:“四舍五入”,算法为Math.floor(x+0.5)。Math.round(11.5),结果为:12;Math.round(-11.5),结果为:-11。

 

9、抽象类是可以有静态的main方法

1 public abstract class AbstractClass {
2     public static String name;
3     
4     public abstract void eat();
5     
6     public static void main(String[] args) {
7         System.out.println("这是main方法。。。");
8     }
9 }

 

10、构造器Constructor是否可以被override?

  构造器Constructor不能被继承,因此不能被Override,但可以被Overload。

1 public final class Constructor<T> extends AccessibleObject implements GenericDeclaration, Member {
2     //...  ...  
3 }

 

11、访问权限:public、private、protected和默认

                   当前类        同一package       子孙类       其他package

    public            Y                   Y                   Y                 Y

    protected      Y                   Y                   Y                 N

    friendly         Y                   Y                   N                 N

    private         Y                   N                   N                 N

  注:friendly并不是java的关键字,只有当变量前面没有写明任何访问权限修饰符时,就默认以friendly作为访问权限。

  protected和friendly只允许在同一个包中的其他类访问,但是protected可以通过子类继承的方式访问其他包中的父类,但是friendly则不可以,这也是两者的唯一区别。

 

12、写clone()方法时,通常都有一行代码,是什么?

  super.clone();因为首先要把父类中的成员复制到位,然后才是复制自己的成员。

  http://www.cnblogs.com/o-andy-o/archive/2012/04/06/2434904.html

  在实际编程过程中,我们常常要遇到这种情况:有一个对象A,在某一时刻A中已经包含了一些有效值,此时可能 会需要一个和A完全相同新对象B,并且此后对B任何改动都不会影响到A中的值,也就是说,A与B是两个独立的对象,但B的初始值是由A对象确定的。在 Java语言中,用简单的赋值语句是不能满足这种需求的。要满足这种需求虽然有很多途径,但实现clone()方法是其中最简单,也是最高效的手段。

  Java的所有类都默认继承java.lang.Object类,在java.lang.Object类中有一个方法clone()。JDK API的说明文档解释这个方法将返回Object对象的一个拷贝。要说明的有两点:一是拷贝对象返回的是一个新对象,而不是一个引用。二是拷贝对象与用 new操作符返回的新对象的区别就是这个拷贝已经包含了一些原来对象的信息,而不是对象的初始信息。

  一个很典型的调用clone()代码如下:

 1 class CloneClass implements Cloneable{
 2  public int aInt;
 3  public Object clone(){
 4   CloneClass o = null;
 5   try{
 6    o = (CloneClass)super.clone();
 7   }catch(CloneNotSupportedException e){
 8    e.printStackTrace();
 9   }
10   return o;
11  }
12
1 public class CloneClass implements Cloneable {
2     
3 }
1 public class Object{
2     ... ...
3     protected native Object clone() throws CloneNotSupportedException;
4     ... ...    
5 }

  有三个值得注意的地方,一是希望能实现clone功能的CloneClass类实现了Cloneable接口,这个接口属于java.lang 包,java.lang包已经被缺省的导入类中,所以不需要写成java.lang.Cloneable。另一个值得请注意的是重载了clone()方法。最后在clone()方法中调用了super.clone(),这也意味着无论clone类的继承结构是什么样的,super.clone()直接或间接调用了java.lang.Object类的clone()方法。下面再详细的解释一下这几点。

  应该说第三点是最重要的,仔细观察一下Object类的clone()一个native方法,native方法的效率一般来说都是远高于java中的非native方法。这也解释了为什么要用Object中clone()方法不是先new一个类,然后把原始对象中的信息赋到新对象中,虽然这也实现了clone功能。对于第二点,也要观察Object类中的clone()还是一个protected属性的方法。这也意味着如果要应用clone()方法,必须继承Object类,在 Java中所有的类是缺省继承Object类的,也就不用关心这点了。然后重写clone()方法。还有一点要考虑的是为了让其它类能调用这个clone 类的clone()方法,重写之后要把clone()方法的属性设置为public

  那么clone类为什么还要实现 Cloneable接口呢?稍微注意一下,Cloneable接口是不包含任何方法的其实这个接口仅仅是一个标志,而且这个标志也仅仅是针对 Object类中clone()方法的,如果clone类没有实现Cloneable接口,并调用了Object的clone()方法(也就是调用了 super.Clone()方法),那么Object的clone()方法就会抛出CloneNotSupportedException异常。

  注意:Cloneable这是java很经典的标示接口,还有序列化接口Serializable也是什么方法也没有!它们的作用就是告诉虚拟机它们现在已经被标示了,可以进行相应的处理了!

 

13、面向对象的特征

  面向对象编程语言有封装、继承、抽象、多态4个主要的特征。

  1)封装:封装是保证软件部件具有优良的模块性的基础,封装的目标就是要实现软件部件的“高内聚、低耦合”,防止程序相互依赖而带来的变动影响。在面向对象的编程语言中,对象是封装的基本单位。面向对象的封装就是把描述一个对象的属性和行为的代码封装到一个“模块”中,即一个类中。只要把变量和访问这个变量的方法放在一起,将一个类中的成员变量全部定义为私有的,只有这个类的方法可以访问,这就基本上实现了对象的封装。

  2)抽象:抽象就是找出一些事物的相似和共性之处,然后将这些事物归为一个类,这个类只考虑这些事物的相似和共性之处,并且会忽略与当前主题和目标无关的哪些部分,将注意力集中在与当前目标有关的方面。

  3)继承:继承是子类自动共享父类数据和方法的机制,这是类之间的一种关系,提高了软件的重用性和可扩展性。

4)多态:多态是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行时才确定,即一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在程序运行期间才能决定。因为在程序运行时才能确定具体的类,这样,不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,从而导致该引用调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。方法的重写Overriding和重载Overloading是Java多态性的不同表现。重写Overriding是父类与子类之间多态性的一种表现,、重载Overloading是一个类中多态性的一种表现。

 

14、abstract class和interface的区别

  接口中所有的方法必须都是抽象的,接口中的方法定义默认为public abstract类型,接口中的成员变量类型默认为public static final。

  语法上的区别:

    1、抽象类可以有构造方法,接口中不能有构造方法。

    2、抽象类中可以有普通成员变量,接口中没有普通成员变量。

    3、抽象类中可以包含非抽象的普通方法,接口中的所有方法都必须是抽象的,不能有非抽象的普通方法。

    4、抽象类中的抽象方法的访问类型可以使public、protected,但接口中的所有抽象方法只能是public类型的,并且默认即为public abstract类型。

    5、抽象类中可以包含静态方法,接口中不能包含静态方法。

    6、抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以使任意的,但接口中定义的变量只能是public static final类型,并且默认即为public static final类型。

    7、一个类可以实现多个接口,但只能继承一个抽象类。

 

15、String s="a"+"b"+"c"+"d";共创建了多少个对象?

  只创建了一个String对象。

你可能感兴趣的:(java基础)