欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java面试宝典

程序员文章站 2024-03-23 14:50:34
...

JavaSE基础篇

Java面向对象
面向对象都有哪些特性以及你对这些特性的理解。
  • 继承:继承是从已有类得到继承信息创建新类的过程。提供继承信息的类被称为父类(超类、基类);得到继承信息的类被称为子类(派生类)。继承让变化中的软件系统有了一定的延续性,同事继承也是封装程序中可变因素的重要手段。
  • 封装:封装是把数据和操作数据的方法绑定起来,数据只能通过已定义的接口访问。面对对象的本质就是将现实世界描绘成一系列完全自治、封闭的对象。编写一个类就是对数据和数据操作的封装;在类中编写的方法就是对实现细节的一种封装。简言之,封装就是隐藏一切可以隐藏的东西,只向外界提供最简单的编程接口。
  • 多态:多态是允许不同子类型的对象对同一消息作出不同的响应。简言之,用同样的对象引用调用同样的方法但是做了不同的事情。

    多态分为编译时多态和运行时多态:
    1.方法的重载(Overload)实现的就是编译时的多态也称为前绑定。
    2.方法的重写(Override)实现的是运行时的多态也称为后绑定。(如果将对象的方法视为对象向外界提供的服务,那么运行时的多态可以解释为:当A系统访问B系统提供服务时,B系统有多重提供服务的方式,但一切对A系统来说都是透明的。)
    运行时的多态是面对对象最精髓的东西,要实现多态需要做两件事:
    1、方法的重写(子类继承父类并重写父类中已有的或抽象的方法);
    2、对象造型(用父类引用子类对象,这样引用调用同样的方法就会根据子类对象的不同而表现出不同的行为)。

  • 抽象:抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面。抽象只关注对象有哪些属性和行为,并不关注这些行为的细节是什么。

注意:默认情况下面向对象有3大特性,封装、继承、多态,4大特性,把抽象加上去。

访问权限修饰符public、private、protected, 以及不写(默认)时的区别。
修饰符 当前类 同包 子类 其他包
public
protected ×
default × ×
private × × ×
如何理解clone对象
  • 为什么要用clone:
    在编程过程中,遇到这种情况:有一个对象A,A中包含了一些有效值,此刻需要一个和A对象完全相同的新对象B,而对B对象的任何改动不会影响到A对象中的值,即A与B是两个独立的对象,但B对象的初始值由A对象确定。(实现clone()方法是其中最简单,也是最高效的手段。)
  • new一个对象的过程和clone一个对象的过程区别
    1、new操作符的本意是分配内存。程序执行到new操作符时,首先去看new操作符后面的类型,因为知道了类型,才能知道要分配多大的内存空间。分配完内存之后,再调用构造函数,填充对象的各个域,这一步叫做对象的初始化,构造方法返回后,一个对象创建完毕,可以把他的引用(地址)发布到外部,在外部就可以使用这个引用操纵这个对象。
    2、clone在第一步是和new相似的,都是分配内存,调用clone方法时,分配的内存和原对象(即调用clone方法的对象)相同,然后再使用原对象中对应的各个域,填充新对象的域,填充完成之后,clone方法返回,一个新的相同的对象被创建,同样可以把这个新对象的引用发布到外部。
JavaSE语法
.java源文件中是否可以包括多个类(不是内部类)?有什么限制?

可以有多个类,但只能有一个public类,且public类名和文件名相同。

Java 有没有 goto 语句?

goto 是 Java 中的保留字,在目前版本的 Java 中没有使用。

& 和 && 异同。

&运算符有两种用法:(1)按位与;(2)逻辑与。&&运算符是短路与。
同:二者都要求运算符左右两端的布尔值都是TRUE整个表达式的值才是 TRUE。
异:如果&&左边的表达式的值是 FALSE,右边的表达式会被直接短路掉,不会进行运算。

1.用&&多于&,例如在验证用户登录时判定用户名不是 null 而且不是空字符串,应当写为 username != null&&!username.equals(""),二者的顺序不能交换,更不能用&运算符,因为第一个条件如果不成立,根本不能进行字符串的equals 比较,否则会产生 NullPointerException异常。
2.3!=3&&++y>0 y不会自增长;3!=3&++y>0 y会自增长;3&2(位运算)结果为5。
注意:逻辑或运算符(|)和短路或运算符(||)的差别也是如此。

在 Java 中,如何跳出当前的多重嵌套循环?
  • 方法一:在最外层循环前加一个标记如 A,然后用 break A,可以跳出多重循环。
ok:
for(inti=0;i<10;i++){
   for(intj=0;j<10;j++){
       System.out.println(“i=”+ i + “,j=” + j);
          if(j == 5) 
          break ok;
   }
}
  • 方法二:通过标识作为循环条件,用break跳出。
int arr[][] = {{1,2,3},{4,5,6,7},{9}};
boolean found = false;
for(int i=0;i<arr.length &&!found;i++){
   for(intj=0;j<arr[i].length;j++){
      System.out.println(“i=”+ i + “,j=” + j);
      if(arr[i][j]== 5){
         found= true;
         break;
      }
   }
}
两个对象值相同 (x.equals(y) == true) ,但却可有不同的hashCode这句话对不对?

不对,如果两个对象 x 和 y 满足 x.equals(y) == true,它们的哈希码(hashCode)应当相同。

Java 对于 eqauls 方法和 hashCode 方法是这样规定的:
(1)如果两个对象相同(equals 方法返回 true),那么它们的 hashCode 值一定相同
(2)如果两个对象的 hashCode 相同,它们并不一定相同。


equals 方法必须满足:
(1)自反性(x.equals(x)必须返回 true)、
(2)对称性(x.equals(y)返回 true 时,y.equals(x)也必须返回 true)
(3)传递性(x.equals(y)和 y.equals(z)都返回 true 时,x.equals(z)也必须返回 true)
(4)一致性(当x 和 y 引用的对象信息没有被修改时,多次调用 x.equals(y)应该得到同样的返回值),而且对于任何非 null 值的引用 x,x.equals(null)必须返回 false。


实现高质量的 equals 方法的诀窍包括:
(1)使用==操作符检查”参数是否为这个对象的引用”;
(2)使用 instanceof 操作符检查”参数是否为正确的类型”;
(3)对于类中的关键属性,检查参数传入对象的属性是否与之相匹配;
(4)编写完 equals 方法后,问自己它是否满足对称性、传递性、一致性;
(5)重写 equals 时总是要重写 hashCode;
(6)不要将 equals 方法参数中的 Object 对象替换为其他的类型,在重写时不要忘掉
@Override 注解。

是否可以继承 String

String 类是 final 类,不可以被继承。继承 String 本身就是一个错误的行为,对 String 类型最好的重用方式是关联关系(Has-A)和依赖关系(Use-A)而不是继承关系(Is-A)。

当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?

值传递。

Java 语言的方法调用只支持参数的值传递。当一个对象实例作为一个参数被传递到方法中时,参数的值就是对该对象的引用。对象的属性可以在被调用过程中被改变,但对对象引用的改变是不会影响到调用者的。
C++和 C#中可以通过传引用或传输出参数来改变传入的参数的值。说明:Java 中没有传引用实在是非常的不方便,这一点在 Java 8 中仍然没有得到改进,正是如此在 Java 编写的代码中才会出现大量的 Wrapper 类(将需要通过方法调用修改的引用置于一个 Wrapper 类中,再将 Wrapper 对象传入方法),这样的做法只会让代码变得臃肿,尤其是让从 C 和 C++转型为 Java 程序员的开发者无法容忍。

重载(overload)和重写(override)的区别?重载的方法能否根据返回类型进行区分?
  • 定义:方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。
  • 重载:重载发生在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)则视为重载;
  • 重写:重写发生在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的返回类型,比父类被重写方法更好访问,不能比父类被重写方法声明更多的异常(里氏代换原则)。重载对返回类型没有特殊的要求。

  • 方法重载的规则:

    1. 方法名一致,参数列表中参数的顺序,类型,个数不同。
    2. 重载与方法的返回值无关,存在于父类和子类,同类中。
    3. 可以抛出不同的异常,可以有不同修饰符。
  • 方法重写的规则:

    1. 参数列表必须完全一致,返回类型必须完全一致。
    2. 构造方法不能被重写,声明为 final 的方法不能被重写,声明为 static 的方法不能被重写,但是能够被再次声明。
    3. 访问权限不能比父类中被重写的方法的访问权限更低。
    4. 重写的方法能够抛出任何非强制异常(UncheckedException,也叫非运行时异常),无论被重写的方法是否抛出异常。但是重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。
为什么函数不能根据返回类型来区分重载?

因为调用时不能指定类型信息,编译器不知道你要调用哪个函数。

//例一:
/**
 *当调用 max(1, 2);时无法确定调用的是哪个,单从这一点上来说,仅返回值类型不同的重载是不应该允许的。
 */
float max(int a, int b);
int max(int a, int b);

//例二:
/**
 * 若编译器可根据上下文(语境)明确判断出含义,比如在 int x=f()中,那么这样做完全没有问题。
 * 然而,我们也可能调用一个方法,同时忽略返回值;我们通常把这称为“为它的副作用去调用一个方法”,因为我们关心的不是返回值,而是方法调用的其他效果。
 * 所以假如我们像下面这样调用方法: f(); Java 怎样判断 f()的具体调用方式呢?而且别人如何识别并理解代码呢?由于存在这一类的问题,所以不能。
 * 函数的返回值只是作为函数运行之后的一个“状态”,他是保持方法的调用者与被调用者进行通信的关键。 并不能作为某个方法的“标识”。
 */
void f(){}
int f(){}
char 型变量中能不能存储一个中文汉字,为什么?

char 类型可以存储一个中文汉字,因为 Java 中使用的编码是 Unicode(不选择任何特定的编码,直接使用字符在字符集中的编号,这是统一的唯一方法),一个 char 类型占 2 个字节(16 比特),所以放一个中文是没问题的。

补充:使用 Unicode 意味着字符在 JVM 内部和外部有不同的表现形式,在 JVM 内部都是 Unicode,当这个字符被从 JVM内部转移到外部时(例如存入文件系统中),需要进行编码转换。所以 Java 中有字节流和字符流,以及在字符流和字节流之间进行转换的转换流,如InputStreamReader 和OutputStreamReader,这两个类是字节流和字符流之间的适配器类,承担了编码转换的任务;对于 C程序员来说,要完成这样的编码转换恐怕要依赖于 union(联合体/共用体)共享内存的特征来实现了。