夜光:Java成神之路(三)擅长的语言

夜光序言:

 

 

我多么想等你回来,但我却先白了头

 

 

 

 
 
正文:
 
                                              以道御术 / 以术识道

 


 


 

方法重写的规则:

 
1.参数列表必须完全与被重写方法的一致,返回类型必须完全与被重写方法的返回类型一致。
 
2.构造方法不能被重写,声明为 final 的方法不能被重写,声明为 static 的方法不能被重写,但是能够被再次声明。
 

3.访问权限不能比父类中被重写的方法的访问权限更低。

 
4.重写的方法能够抛出任何非强制异常(UncheckedException,也叫非运行时异常),无论被重写的方法是否抛出异常。

 

但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以

 
 
 

 

8. 为什么函数不能根据返回类型来区分重载?

 

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

 

例如:

 

1float max(int a, int b);
 
2int max(int a, int b);
 
当调用 max(1, 2);时无法确定调用的是哪个,单从这一点上来说,仅返回值类型不同的重载是不应该允许的。
再比如对下面这两个方法来说,虽然它们有同样的名字和自变量,但其实是很容易区分的:
 
1void f() {}
 
2int f() {}
 
 
若编译器可根据上下文(语境)明确判断出含义,比如在 int x=f()中,那么这样做完全没有问题。
 
然而,我们也可能调用一个方法,同时忽略返回值;我们通常把这称为“为它的副作用去调用一个方法”,因为我们关心的不是返回值,而是方法调用的其他效果。
 
所以假如我们像下面这样调用方法: f();
 
Java 怎样判断 f()的具体调用方式呢?而且别人如何识别并理解代码呢?
 
由于存在这一类的问题,所以不能。
 
 
函数的返回值只是作为函数运行之后的一个“状态”,他是保持方法的调用者与被调用者进行通信的关键。
 
并不能作为某个方法的“标识”

 


 

9. char 型变量中能不能存储一个中文汉字,为什么?

 

char 类型可以存储一个中文汉字,因为 Java 中使用的编码是 Unicode
 
(不选择任何特定的编码,直接使用字符在字符集中的编号,这是统一的唯一方法)
 
一个 char 类型占 2 个字节(16 比特),所以放一个中文是没问题的。
 
 
补充:使用 Unicode 意味着字符在 JVM 内部和外部有不同的表现形式,在 JVM 内部都是 Unicode,当这个字符被从 JVM 内部转移到外部时(例如存入文件系统中),需要进行编码转换。
 
所以 Java 中有字节流和字符流,以及在字符流和字节流之间进行转换的转换流
 
 
如 InputStreamReader OutputStreamReader,这两个类是字节流和字符流之间的适配器类,承担了编码转换的任务;
 
 
对于 C 程序员来说,要完成这样的编码转换恐怕要依赖于 union(联合体/共用体)共享内存的特征来实现了。

 


 

10. 抽象类(abstract class)和接口(interface)有什么异同?

不同:

 

抽象类:

1.抽象类中可以定义构造器
 
2.可以有抽象方法和具体方法
 
3.接口中的成员全都是 public 的
 
4.抽象类中可以定义成员变量
 
5.有抽象方法的类必须被声明为抽象类,而抽象类未必要有抽象方法
 
6.抽象类中可以包含静态方法
 
7.一个类只能继承一个抽象类
 

 

接口:

 
1.接口中不能定义构造器
 
2.方法全部都是抽象方法
 
3.抽象类中的成员可以是 private、默认、protected、public
 
4.接口中定义的成员变量实际上都是常量
 
5.接口中不能有静态方法
 
6.一个类可以实现多个接口     兄弟,对比看下继承,所以我们程序员一般都是面向接口开发
 
 

 

相同:

 
1.不能够实例化
 
2.可以将抽象类和接口类型作为引用类型
 
3.一个类如果继承了某个抽象类或者实现了某个接口都需要对其中的抽象方法全部进行实现,否则该类仍然需要被声明为抽象类

 

 

 


 

 

11. 抽象的(abstract)方法是否可同时是静态的(static), 是否可同时是本地方法(native),是否可同时被 synchronized

 

都不能。

 
抽象方法需要子类重写,而静态的方法是无法被重写的,因此二者是矛盾的。
 
本地方法是由本地代码(如 C 代码)实现的方法,而抽象方法是没有实现的,也是矛盾的。
 
 
synchronized 和方法的实现细节有关, 抽象方法不涉及实现细节,因此也是相互矛盾的。

 


 

12. 阐述静态变量和实例变量的区别?

 

静态变量: 是被 static 修饰符修饰的变量,也称为类变量,它属于类,不属于类的任何一个对象,一个类不管创建多少个对象,静态变量在内存中有且仅有一个拷贝;

 

实例变量: 必须依存于某一实例,需要先创建对象然后通过对象才能访问到它。静态变量可以实现让多个对象共享内存

 


 

13. ==和 equals 的区别?

equals 和== 最大的区别是一个是方法一个是运算符。

 
==:如果比较的对象是基本数据类型,则比较的是数值是否相等;如果比较的是引用数据类型,则比较的是对象的地址值是否相等。
 

 

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

 

 
注意:equals 方法不能用于基本数据类型的变量,如果没有对 equals 方法进行重写,则比较的是引用类型的变量所指向的对象的地址
 

 

 

14. break 和 continue 的区别?

 

break 和 continue 都是用来控制循环的语句。
 
break 用于完全结束一个循环,跳出循环体执行循环后面的语句。
 
continue 用于跳过本次循环,执行下次循环。
 
 

 

15. String s = "Hello";s = s + " world!";这两行代码执行后,原始的 String 对象中的内容到底变了没有?

 

答案是没有

 
因为 String 被设计成不可变(immutable)类,所以它的所有对象都是不可变对象。在这段代码中,s 原先指向一个 String 对象,内容是 "Hello",然后我们对 s 进行了“+”操作,那么 s 所指向的那个对象是否发生了改变呢? --没有
 
 

这时,s 不指向原来那个对象了,而指向了另一个 String 对象,内容为"Hello world!",原来那个对象还存在于内存之中,只是 s 这个引用变量不再指向它了。

 

通过上面的说明,我们很容易导出另一个结论,如果经常对字符串进行各种各样的修改,或者说,不可预见的修改,那么使用 String 来代表字符串的话会引起很大的内存开销。
 
因为 String 对象建立之后不能再改变,所以对于每一个不同的字符串,都需要一个 String 对象来表示。这时,应该考虑使用 StringBuffer 类,它允许修改,而不是每个不同的字符串都要生成一个新的对象。并且,这两种类的对象转换十分容易。
 
 

同时,我们还可以知道,如果要使用内容相同的字符串,不必每次都 new 一个 String。

 
 
例如我们要在构造器中对一个名叫 s 的 String 引用变量进行初始化,把它设置为初始值,应当这样做
 
public class Demo {
 private String s;
  ...
 s = "Initial Value";
 ...
}
而非
s = new String("Initial Value");

 

后者每次都会调用构造器,生成新对象,性能低下且内存开销大,并且没有意义,因为 String 对象不可改变,所以对于内容相同的字符串,只要一个 String 对象来表示就可以了。
 
也就说,多次调用上面的构造器创建多个对象,们的 String 类型属性 s 都指向同一个对象。
 
 
上面的结论还基于这样一个事实:对于字符串常量,如果内容相同,Java 认为它们代表同一个 String 对象。
而用关键字 new 调用构造器,总是会创建一个新的对象,无论内容是否相同
 
至于为什么要把 String 类设计成不可变类,是它的用途决定的。其实不只 String,很多 Java 标准类库中的类都是不可变的。
 
在开发一个系统的时候,我们有时候也需要设计不可变类,来传递一组相关的值,这也是面向对象思想的体现。
不可变类有一些优点,比如因为它的对象是只读的,所以多线程并发访问也不会有任何问题。
 
 
当然也有一些缺点,比如每个不同的状态都要一个对象来代表,可能会造成性能上的问题。
 
所以 Java 标准类库还提供了一个可变版本,即 StringBuffer。
 
 
 
 
 
 
 

 

 

三、Java 中的多态

 
 

1. Java 中实现多态的机制是什么?

靠的是父类或接口定义的引用变量可以指向子类或具体实现类的实例对象
 
而程序调用的方法在运行期才动态绑定,就是引用变量所指向的具体实例对象的方法
 

也就是内存里正在运行的那个对象的方法,而不是引用变量的类型中定义的方法。

 
 
 

 

 

 

四、Java 的异常处理

 

 

1. Java 中异常分为哪些种类

1)按照异常需要处理的时机分为编译时异常(也叫强制性异常)也叫 CheckedException 和运行时异常(也叫非强制性异常)也叫 RuntimeException。
 
只有 java 语言提供了 Checked 异常,Java 认为 Checked 异常都是可以被处理的异常,所以 Java 程序必须显式处理 Checked 异常。
 
如果程序没有处理 Checked 异常,该程序在编译时就会发生错误无法编译。
 

这体现了 Java 的设计哲学:没有完善错误处理的代码根本没有机会被执行。对 Checked 异常处理方法有两种:

 
1 当前方法知道如何处理该异常,则用 try...catch 块来处理该异常。
 
2 当前方法不知道如何处理,则在定义该方法是声明抛出该异常。
 
运行时异常只有当代码在运行时才发行的异常,编译时不需要 try catch。
 
Runtime 如除数是 0 和数组下标越界等,其产生频繁,处理麻烦,若显示申明或者捕获将会对程序的可读性和运行效率影响很大。
 
所以由系统自动检测并将它们交给缺省的异常处理程序。当然如果你有处理要求也可以显示捕获它们。
 
 

 


 

2. 调用下面的方法,得到的返回值是什么

1. public int getNum(){
2. try {
3. int a = 1/0;
4. return 1;
5. } catch (Exception e) {
6. return 2;
7. }finally{
8. return 3;
9. }

 

代码在走到第 3 行的时候遇到了一个 MathException,这时第四行的代码就不会执行了,代码直接跳转到 catch语句中,走到第 6 行的时候,异常机制有这么一个原则如果在 catch 中遇到了 return 或者异常等能使该函数终止的话
 
那么有 finally 就必须先执行完 finally 代码块里面的代码然后再返回值。
 
因此代码又跳到第 8 行,可惜第 8 行是一个 return 语句,那么这个时候方法就结束了,因此第 6 行的返回结果就无法被真正返回。
 
 
如果 finally 仅仅是处理了一个释放资源的操作,那么该道题最终返回的结果就是 2。因此上面返回值是 3

 

 


 

3. error 和 exception 的区别?

 

Error 类和 Exception 类的父类都是 Throwable 类,他们的区别如下。
 
Error 类一般是指与虚拟机相关的问题,如系统崩溃,虚拟机错误,内存空间不足,方法调用栈溢出等。
 
对于这类错误的导致的应用程序中断,仅靠程序本身无法恢复和和预防,遇到这样的错误,建议让程序终止。
 
Exception 类表示程序可以处理的异常,可以捕获且可能恢复。遇到这类异常,应该尽可能处理异常,使程序恢复运行,而不应该随意终止异常。
 
Exception 类又分为运行时异常(Runtime Exception)和受检查的异常(Checked Exception )
 
运行时异常;
 
ArithmaticException,IllegalArgumentException,编译能通过,但是一运行就终止了,程序不会处理运行时异常,
 
出现这类异常,程序会终止。
 
 
而受检查的异常,要么用 try。。。catch 捕获,要么用 throws 字句声明抛出,交给它的父类处理,否则编译不会通过。
 
 

 

4. java 异常处理机制

 

Java 对异常进行了分类,不同类型的异常分别用不同的 Java 类表示,所有异常的根类为 java.lang.Throwable,Throwable 下面又派生了两个子类:Error 和 Exception,Error 表示应用程序本身无法克服和恢复的一种严重问题。
 
 
 
Exception 表示程序还能够克服和恢复的问题,其中又分为系统异常和普通异常,系统异常是软件本身缺陷所导致的问题,也就是软件开发人员考虑不周所导致的问题
 
 
软件使用者无法克服和恢复这种问题,但在这种问题下还可以让软件系统继续运行或者让软件死掉

 

例如,数组脚本越界(ArrayIndexOutOfBoundsException)

空指针异常(NullPointerException)

类转换异常(ClassCastException)

 
 
普通异常是运行环境的变化或异常所导致的问题, 是用户能够克服的问题,例如,网络断线,硬盘空间不够,发生这样的异常后,程序不应该死掉。
 
 
java 为系统异常和普通异常提供了不同的解决方案,编译器强制普通异常必须 try..catch 处理或用 throws 声明继 续抛给上层调用方法处理,所以普通异常也称为 checked 异常,而系统异常可以处理也可以不处理
 
 

所以,编译器不强制用 try..catch 处理或用 throws 声明,所以系统异常也称为 unchecked 异常。

 

 

 


 

5. 请写出你最常见的 5 个 RuntimeException

 

下面列举几个常见的 RuntimeException。

 
1)java.lang.NullPointerException 空指针异常;出现原因:调用了未经初始化的对象或者是不存在的对象。
 
2)java.lang.ClassNotFoundException 指定的类找不到;出现原因:类的名称和路径加载错误;通常都是程序试图通过字符串来加载某个类时可能引发异常。
 
3)java.lang.NumberFormatException 字符串转换为数字异常;出现原因:字符型数据中包含非数字型字符。
 
4)java.lang.IndexOutOfBoundsException 数组角标越界异常,常见于操作数组对象时发生。
 
5)java.lang.IllegalArgumentException 方法传递参数错误。
 
6)java.lang.ClassCastException 数据类型转换异常
 
7)java.lang.NoClassDefFoundException 未找到类定义错误。
 
8)SQLException SQL 异常,常见于操作数据库时的 SQL 语句错误。
 
9)java.lang.InstantiationException 实例化异常。
 
10)java.lang.NoSuchMethodException 方法不存在异常。

 


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章