[Think In Java]基础拾遗2 - 多态、反射、异常、字符串
目录
第八章 多态
第十四章 类型信息
第十二章 通过异常处理错误
第十三章 字符串
第八章 多态
1. 前期绑定 & 后期绑定
绑定是指将方法调用同一个方法主体关联起来的这么一个过程。如果在程序执行前进行绑定,就称为前期绑定。(C中所有的方法调用都是前期绑定)
后期绑定就是指在运行时根据对象的类型进行绑定。后期绑定也叫动态绑定/运行时绑定。
一种语言要想实现后期绑定,就必须具有某种机制,以便于在运行时能判断对象的类型,从而调用恰当的方法。不同的语言有不同的实现机制,不管怎么样都必须在对象中安置某种类型信息。(比如在C++中是通过在对象中安插一个指向虚函数表的指针来保存该对象的类型信息)。由此可见,运行时类型信息很重要!那java中是如何实现在运行时来获取对象的类型信息的呢?
java中除了static方法和final方法(private方法属于final方法)之外,其他所有的方法都是后期绑定的。声明为final的方法会告诉编译器“关闭”动态绑定。
2. 不会产生多态的情况
(1)子类覆盖父类的private方法
(2)访问字段(用一个指向子类的父类引用访问字段是不会产生多态的),并且这个访问是在编译时解析。
(3)静态方法
3. 如果覆盖了父类的方法,特指资源清理类方法,记得先释放子类部分的资源,然后super调用父类的方法以释放对象的父类部分资源。
4. RTTI
在运行期间对类型进行检查的行为称作运行时类型识别。
第十四章 类型信息
运行时类型信息使得我们可以在运行时发现和使用类型信息。
1. java是如何让我们在运行时识别对象和类的信息的呢?
两种方式:
(1)传统的RTTI:它假定我们在编译时已经知道了所有的类型。(也就是说:在编译的时候就知道在某个类继承体系中有哪些类型了。)
(2)反射机制:它允许我们在运行时发现和使用类的信息。
2. class对象
要理解RTTI在java中的工作原理,首先必须知道类型信息在运行时是如何表示的。这项工作是由称为class对象的特殊对象完成的,它包含了与该类有关的信息。
[个人理解:编程语言中的所谓对象,无非就是内存中的一块区域,那么class对象就是这样的一块内存,它里面保存的是类相关的信息。]
类是程序的一部分,每个类都有一个class对象。
[个人理解:一个类有哪些方法,有哪些域,所有的这些信息都应该保存在内存中的一个区域,每个类在内存中都应该有这样的一块区域来保存这样的信息,而每一个这样的一块区域被称为class对象。]
3. java特有的动态加载
java中所有的类都是在对其第一次使用时,动态加载到JVM中的。所谓动态加载是指:java程序在它开始运行之前并非被完全加载,其各个部分是指在必需时才加载的。这一点与许多传统语言都不同。C++是静态加载语言。
4. 为什么要先有class对象?
class对象保存了对象的类型信息,如果没有class对象,就不知道对象应该占据多大的内存,在new对象的时候就不知道该分配多大的空间。所以在创建对象实例之前JVM一定会保证已经加载了对应的类。一旦一个类的class对象在内存中被创建好了,它就被用来创建这个类的所有对象。
5. 获取某个类A的class对象的引用
(1)public static Class<?> forName(String name) throws ClassNotFoundException
返回与带有给定字符串名的类或接口相关联的 Class 对象。
这种方式适用于没有对象实例的情况。
(2)调用A对象实例的getClass()方法
这种方式适用于有对象实例的情况。
(3)类字面常量 "A.class"
这种方式简单、安全,在编译时就会受到检查。
6. 为了使用一个类而做的准备(类的加载过程):
(1)加载:在内存中创建一个class对象。
(2)链接:验证字节码、为静态域分配存储空间、解析对其他类的所有引用
(3)初始化:初始化静态域的存储空间
7. 在java中哪些情况下需要RTTI呢?
(1)传统的类型转换:由RTTI确保类型转换的正确性,若执行了一个错误的类型转换,会抛出ClassCastException异常。
(2)查询Class对象相关的操作。
(3)instanceof:用法是 ”对象 instanceof 类型“ ,可以使用Class类的实例方法boolean isInstance(Object obj)判定指定的 Object 是否与此 Class 所表示的对象赋值兼容。此方法是 Java 语言 instanceof 运算符的动态等效方法。即对于类A来说,下面的两个if语句等效:
A a = new A();
if(a instanceof A)
{
} if(A.class.isInstance(a))
{
}
8. instanceof 和 class的等价性
用instanceof保持了类型的概念,表示“你是这个类吗,或者你是这个类的派生类吗?”
用==比较实际的Class对象就没有考虑继承——它或者是这个确切的类型,或者不是。
9 RTTI和反射的区别和联系
相同点:RTTI和反射都是一种让我们在运行时可以识别对象的类型信息的一种机制。
不同点:
RTTI:由编译器在编译时打开和检查*.class文件。
反射机制:由JVM在运行时打开和检查*.class文件。
10. 个人对反射和RTTI的理解
(1)RTTI:
在编译的时候,编译器javac干的事情就是将java源文件转换成.class文件,每个具体的类可以还是引用其他类,在这个阶段javac编译器就会验证对某个类的调用是否合法之类的,如果在源程序中调用了某个类没有的方法,此时编译器就会报错的。在整个编译阶段,编译器必须要知道某个类的.class文件,否则不知道该类有哪些方法,就不能验证调用是否合法。这样的话在编译阶段,编译器就已经知道了各个类的类型,当然在运行的时候也就能提供给我们在运行时查询某个对象的类型了。
Class clazz1 = Class.forName("rtti.Apple"); //在编译阶段编译器不会检查和打开字符串"rtti.Apple"指定的类对应的.class文件,而是在运行期间由JVM加载。
Fruit fruit = new Bnana(); //编译阶段编译器会去查找Fruit类和Apple类各自对应的.class文件,以确定在给定对象上的方法调用是否合法。
fruit.setWeight(10);
Class clazz2 = fruit.getClass();
System.out.println(clazz2.getName());
(2)反射:
由于没有了.class文件,就不能new对象,不能调方法,肿么办呢?我们仔细思考一下,在java中当我们获得了一个类后不外乎有如下这些操作:
(1) 创建对象(Constructor)
(2)调用方法(Method)
(3)访问/修改对象的某个字段(Field)
并且我们知道每个类都是由域,方法,构造器等元素组成的。所以java在reflect类库中提供了Field、Method以及Constructor等类型来表示每个类的域,方法,构造器等元素,于是在没有class文件无法直接new对象的情况下,我们只好面对这些组件编程咯。以下是使用这些组件编程的典型流程:
Class clazz = Class.forName("aa"); //获取class对象的引用
Object obj = clazz.newInstance(); //创建对象 for(Field f:clazz.getFields()){
f.setLong(obj, 200); //修改域
}
for(Method m:clazz.getMethods()){
m.invoke(obj, null); //调用方法
}
很明显,在反射机制下,JVM在运行时才会去获取响应的.class文件。
【认识误区纠正】: 以前提到反射机制就想到Class,脑海中渐渐形成了"反射就是Class,Class就是反射"的错误观念。应该讲反射是一种机制,在java中它是由Class以及java.lang.reflect类库提供支持的!也就是说Class只是支持反射机制的一个类而已,不代表反射就是Class,何况在RTTI下也会涉及到Class呢。
总结:Java对反射机制提供了很多支持,使得能够创建一个在编译时完全未知的对象,并调用此对象的方法。
11. 动态代理
Java中的动态代理可以动态地创建代理并动态地处理对所代理方法的调用。
调用静态方法java.lang.reflect.Proxy.newProxyInstance()可以创建动态代理。
第十二章 通过异常处理错误
1. 异常处理理论上有两种基本模型
(1)终止模型
在这种模型中,将假设错误非常关键,以至于程序无法返回到异常发生的地方继续执行。一旦异常被抛出,就表明错误已无法挽回,也不能回来继续执行。java支持这种模型。
(2)恢复模型
异常处理程序的工作是修复错误,然后才重新尝试调用出问题的方法,并认为第二次能成功。java中要想实现该模型,可以把try块放在while循环里,这样就不断地进入try块,直到得到满意的结果。
比如:
class TimeoutException extends Exception { } public class Retry{ public static void main(String[] args) {
int tryTimes = 5;
while (true) {
try {
connect("www.google.com"); } catch (TimeoutException e) {
System.out.println("TimeoutException");
} finally {
if (--tryTimes == 0)
break; // out of "while"
}
}
System.out.println("exit main...");
} private static void connect(String addr) throws TimeoutException {
try {
System.out.println("conneting " + addr + " ...");
Thread.sleep(1000);
throw new TimeoutException();
} catch (InterruptedException e) { }
}
}
/*
输出结果:
conneting www.google.com ...
TimeoutException
conneting www.google.com ...
TimeoutException
conneting www.google.com ...
TimeoutException
conneting www.google.com ...
TimeoutException
conneting www.google.com ...
TimeoutException
exit main...
*/
2. 异常执行流程
例子:
public class ExceptionTest { static void fun() throws Exception
{
System.out.println("ExceptionTest.fun()");
throw new Exception("MyException");
} public static void main(String[] args) { try {
fun();
System.out.println("business");
} catch (Exception e) {
System.out.println("int catch");
}finally{
System.out.println("in finally");
}
System.out.println("exiting ...");
}
}
结果:
ExceptionTest.fun()
int catch
in finally
exiting ...
3. checked异常
在编译时被强制检查的异常被称为被检查的异常(checked异常)。对于这种异常,如果在方法体中throw了这种异常,而方法签名中并没有声明的话,编译器会报错。
4. 重新抛出异常
(1)重新抛出以前的异常
在catch中不能直接throw e,这样以后调用printStackTrace()显示的将是原来异常抛出点的信息,而并非重新抛出点的信息。要想更新这个信息,可以调用fillInStackTrace()方法,它是通过把当前调用栈信息填入原来那个异常对象而建立的。throw (Exception)e.fillStackTrace(); 调用fillStackTrace()的那一行就成了异常的新发生地了。
(2)重新抛出另外一种异常
这样做的话,异常的新发生地就是重新抛出的那一行。也就是说不必按照(1)中那么做了。
例子:
public class Rethrowing {
public static void f() throws Exception {
System.out.println("originating the exception in f()");
throw new Exception("thrown from f()");
}
public static void g() throws Exception {
try {
f();
} catch(Exception e) {
System.out.println("Inside g(),e.printStackTrace()");
e.printStackTrace(System.out);
throw e;
}
}
public static void h() throws Exception {
try {
f();
} catch(Exception e) {
System.out.println("Inside h(),e.printStackTrace()");
e.printStackTrace(System.out);
throw (Exception)e.fillInStackTrace();
}
}
public static void main(String[] args) {
try {
g();
} catch(Exception e) {
System.out.println("main: printStackTrace()");
e.printStackTrace(System.out);
}
try {
h();
} catch(Exception e) {
System.out.println("main: printStackTrace()");
e.printStackTrace(System.out);
}
}
}
输出结果:
originating the exception in f()
Inside g(),e.printStackTrace()
java.lang.Exception: thrown from f()
at Rethrowing.f(Rethrowing.java:7)
at Rethrowing.g(Rethrowing.java:11)
at Rethrowing.main(Rethrowing.java:29)
main: printStackTrace()
java.lang.Exception: thrown from f()
at Rethrowing.f(Rethrowing.java:7)
at Rethrowing.g(Rethrowing.java:11)
at Rethrowing.main(Rethrowing.java:29)
originating the exception in f()
Inside h(),e.printStackTrace()
java.lang.Exception: thrown from f()
at Rethrowing.f(Rethrowing.java:7)
at Rethrowing.h(Rethrowing.java:20)
at Rethrowing.main(Rethrowing.java:35)
main: printStackTrace()
java.lang.Exception: thrown from f()
at Rethrowing.h(Rethrowing.java:24)
at Rethrowing.main(Rethrowing.java:35)
5. 异常链
有时需要捕获一个异常后抛出另一个异常,并且希望将原始异常的信息保存下来,这被称为异常链。
(1)从JDK1.4后,Throwable的子类在构造器中可以接受一个case参数,Throwable(Throwable cause),这个参数用来表示原始异常。这样即使在当前位置创建并抛出了新的异常,也能通过这个异常链追踪到异常最初发生的位置。
(2)只有三种基本的异常类提供了待cause参数的构造器(Exception,Error,RuntimeException)。若要把其他类型异常链接其他,应该用Throwable的initCause(Throwable cause)方法而不是构造器。
Throwable 内部有一个字段:private Throwable cause = this; //用于指示该Throwable 是否是由其他Throwable导致抛出的。
上述(1),(2)两种方式无外乎是设置该字段的值而已。
6. RuntimeException
RuntimeException 是那些可能在 Java 虚拟机正常运行期间抛出的异常的超类。可能在执行方法期间抛出但未被捕获的 RuntimeException 的任何子类都无需在 throws 子句中进行声明。这被称为UnChecked异常。
RuntimeException代表的是编程错误。
第十三章 字符串
1. 不可变String
String对象是不可变的。String类中每一个看起来会修改String值的方法,实际上都是创建了一个全新的String对象。
2. StringBuilder 和 StringBuffer
StringBuilder 是javase5引入的,之前是StringBuffer,StringBuffer是线程安全的。
3. System.out.format类似于C语言中的printf,还有一个格式化类java.util.Formatter专门负责格式化。
[Think In Java]基础拾遗2 - 多态、反射、异常、字符串的更多相关文章
- Java基础拾遗(二)
(尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/76358523冷血之心的博客) 马上就要秋招了,新的一轮笔试面试马上 ...
- Java基础拾遗(一)
(尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/76358391冷血之心的博客) 马上就要秋招了,新的一轮笔试面试马上 ...
- Java基础十二--多态是成员的特点
Java基础十二--多态是成员的特点 一.特点 1,成员变量. 编译和运行都参考等号的左边. 覆盖只发生在函数上,和变量没关系. Fu f = new Zi();System.out.println( ...
- [转帖]java基础学习总结——多态(动态绑定)
https://www.cnblogs.com/xdp-gacl/p/3644035.html 多态的概念 java基础学习总结——多态(动态绑定) 一.面向对象最核心的机制——动态绑定,也叫多态
- Java基础学习小记--多态
题外话:总结了多年的学习心得,不得不说,睡眠是一个学习者的必需品!所谓"早起毁一天"不是没有道理哪,特别对Coders来说,有几天不是加班到夜里.好吧,我承认对于初学Java的我, ...
- JAVA基础知识之JVM-——通过反射查看类信息
Class实例 当类被加载之后,JVM中就会生成一个Class实例,通过这个实例就可以访问JVM中的这个类.有三种方式可以获取Class对象 使用Class的静态方法forName(完整包名) 调用类 ...
- Java基础知识回顾(一):字符串小结
Java的基础知识回顾之字符串 一.引言 很多人喜欢在前面加入赘述,事实上去技术网站找相关的内容的一般都应当已经对相应知识有一定了解,因此我不再过多赘述字符串到底是什么东西,在官网中已经写得很明确了, ...
- 【Java基础】RTTI与反射之Java
一.引言 很多时候我们的程序可能需要在运行时识别对象和类的信息,比如多态就是基于运行时环境进行动态判断实际引用的对象.在运行时识别对象和类的信息主要有两种方式:1.RTTI,具体是Class对象,它假 ...
- JAVA基础 (二)反射 深入解析反射机制
在谈论到反射这个问题时,你是否有例如以下疑问? 不管是在.NET还是Java中反射的原理和机制是一样的,理解了一种还有一种就能够迎刃而解,想要理解反射首先须要了解底层的一些概念和执行.理解了反射有助于 ...
随机推荐
- Starling中通过PivotX 和 PivotY 修改原点
一个显示对象的默认原点在左上角.addChild 是将它的左上角放在了父容器的(0, 0)位置. 如果将该显示对象的PivotX 和 PivotY 修改为其宽高的一半,那么它的原点就变到了该对象的中心 ...
- js 调试
$(":select[name='start_Month'").each(function(item,i){ console.log(item.name + "" ...
- 正则表达式的JS验证
/判断输入内容是否为空 function IsNull(){ var str = document.getElementById('str').value.trim(); ...
- [css]我要用css画幅画(九) - Apple Logo
接着之前的[css]我要用css画幅画(八) - Hello Kitty,这次画的是苹果公司的logo 这次打算将分析和实现步骤尽量详细的说一说. 其实之前的也打算详细讲分析和设计过程,不过之前的图比 ...
- Dictionary摘抄
Dictionary,字典,键值对集合. 下面的代码示例创建一个空的带有字符串键的字符串 Dictionary,并使用 Add 方法添加一些元素.该示例演示在尝试添加重复的键时 Add 方法引发Arg ...
- MS SQL 监控错误日志的告警信息
SQL Server的错误消息(Error Message)按照消息的严重级别一共划分25个等级,级别越高,表示严重性也越高.但是如果你统计sys.messages,你会发现,实际上只有16(SQL ...
- SQL Server中的“最大并行度”的配置建议
SQL Server中的最大并行度(max degree of parallelism)如何设置呢? 设置max degree of parallelism有什么好的建议和指导方针呢?在微软官方文档R ...
- SQL SERVER 2012 修改数据库默认位置不立即生效
今天修改SQL SERVER 2012的数据库默认位置:即数据文件.日志文件默认位置时遇到一个问题,单击"服务器属性"(Server Properties)--> 数据库设置 ...
- php之验证码小程序
验证码功能(个人理解): 减轻服务器的压力(如12306的验证码功能): 防止暴力注册 个人思路:在a-z,A-Z,1-9生成n位随机的数来构成新的验证码. 关于生成验证码的几个小函数 range() ...
- MySQL 复制表
MySQL 复制表 如果我们需要完全的复制MySQL的数据表,包括表的结构,索引,默认值等. 如果仅仅使用CREATE TABLE ... SELECT 命令,是无法实现的. 本文将为大家介绍如何完整 ...