1、令人混淆的构造器

  代码如下格式:

public class Confusing {
private Confusing(Object o) {
System.out.println("Object");
}
private Confusing(double[] dArray) {
System.out.println("double array");
}
public static void main(String[] args) {
new Confusing(null);
}
}

  分析:null这个参数,两个构造器都可以接受,会怀疑这段代码是否会通过编译,运行后发现可以通过编译并且打印出double array?Why?

  正解:知识点——Java重载解析过程是以两个阶段运行的。第一个阶段,选取所有可以获得并且可以使用的方法或构造器。第二阶段,从上一阶段选取的方法或构造器中选取一个最精准的方法。精准的理解——能接受的参数类型越少越精准。例如:本例中的Object,他接收的范围太广了,至少能够把double数组和int数组接受了,所以他就不精准。但是,能接收double数组但不能接收其他数据对象类型例如int,所以就相对来说显得更精准,所以在第二个阶段,选取了相对精准的方法,所以才会有了double的输出。

2、静态方法的覆盖

代码:

class Dog {
public static void bark() {
System.out.print("woof ");
}
} class Basenji extends Dog {
public static void bark() { }
} public class Bark {
public static void main(String args[]) {
Dog woofer = new Dog();
Dog nipper = new Basenji();
woofer.bark();
nipper.bark();
}
}

  先来说明一件事情:nipper编译期类型为Dog类型,运行期类型为Basenji类型。

  分析:静态方法的调用是在编译期选好的,而这个调用当然就要看对象的编译期类型。本例子中,woofer和nipper具有相同的编译类型Dog,虽然nipper在运行期间类型为Basenji,所以他们两个都会打印出woof即打印结果为:woof  woof。

  静态方法的覆盖叫隐藏,一般方法的覆盖叫重载。重载能够得到动态分配的特性(其实就是看运行期的对象类型),隐藏不能得到这种特性(隐藏只能看编译期类型)。所以,如果将Dog和Basenji中的函数方法去掉static,变为重载,那么将会只打印一个woof。

3、instanceof和强制类型转换

三个程序的代码:

public class Type1 {
public static void main(String[] args) {
String s = null;
System.out.println(s instanceof String);
}
} public class Type2 {
public static void main(String[] args) {
System.out.println(new Type2() instanceof String);
}
} public class Type3 {
public static void main(String args[]) {
Type3 t3 = (Type3) new Object();
}
}

(1)instanceof左边对象运行期为null的时候返回false。

(2)instanceof在编译的时候,如果两边都是类(A instanceof  B      A、B都是类),则其中一个必须是另外一个的子类,否则就不能通过编译。很明显本例中的Type2和String没有子类关系,所以不能通过编译。

(3)先讨论下强制类型转换的知识:在Java中由于继承和向上转型,子类可以非常自然地转换成父类,但是父类转换成子类则需要强制转换。因为子类拥有比父类更多的属性、更强的功能,所以父类转换为子类需要强制。这种强制类型转换需要条件的。

父类能强制转换为子类:

  Father father=new Son();//因为Java的继承和向上转型,子类可以很自然地转换为父类

  但是请注意这个Son对象实例在内存中的本质还是Son类型的,只不过它的能力临时被消弱了而已,如果我们想变强怎么办?将其对象类型还原!

  Son son=(Son)father; //这种强制类型转换时可以的。

  其实father引用仍然是Father类型的,只不过是将它的能力加强了,将其加强后转交给son引用了,Son对象实例在son的变量的引用下,恢复真身,可以使用全部功能了。

父类不能强制转换为子类:当引用类型的真实身份(运行期的类型)是父类本身的类型时,强制类型转换就会产生错误。

  Father father=new Father();

  Son son=(Son)father;这个系统会抛出ClassCastException异常信息。

  编译器在编译时只会检查类型之间是否存在继承关系,有则通过;而在运行时就会检查它的真实类型,是则通过,否则抛出ClassCastException异常。

所以在继承中,子类可以自动转型为父类,但是父类强制转换为子类时只有当引用类型真正的身份为子类时才会强制转换成功,否则失败。

4、Java类初始化顺序

  JVM调用main函数时,会触发main函数所在类的初始化。首先,静态域被设置为缺省值(0),接下来按照静态域的出现顺序进行初始化。

  我们大家都知道,对于静态变量、静态初始化块、变量、初始化块、构造器,它们的初始化顺序依次是(这里先假设没有继承) :

          (静态变量、静态初始化块(他们两个初始化顺序是根据写的位置))>(变量、初始化块(他们两个初始化顺序是根据写的位置))>构造器。

  如果有继承的话,是按照这个顺序先去加载父类。

          父类--静态变量  
          父类--静态初始化块  
          子类--静态变量  
          子类--静态初始化块  
          父类--变量  
          父类--初始化块  
          父类--构造器  
          子类--变量  
          子类--初始化块  
          子类--构造器 

5、Java实例初始化顺序

  代码如下:

class Point {
protected final int x, y;
private final String name; // Cached at construction time
Point(int x, int y) {
this.x = x;
this.y = y;
name = makeName();
} protected String makeName() {
return "[" + x + "," + y + "]";
}
public final String toString() {
return name;
}
} public class ColorPoint extends Point {
private final String color;
ColorPoint(int x, int y, String color) {
super(x, y);
this.color = color;
}
protected String makeName() {
return super.makeName() + ":" + color;
}
public static void main(String[] args) {
System.out.println(new ColorPoint(4, 2, "purple"));
}
}

  分析:打印出了[4,2]:null,并不是期望的[4,2]:purple。

  总之,不要在构造器中调用可覆写的方法。在实例初始化中产生的循环将是致命的。该问题的解决方案就是惰性初始化,即当它第一次被使用时初始化,以此取代积极初始化,即当Point实例被创建时初始化,代码如下:

class Point {
protected final int x, y;
private String name; // Lazily initialized
Point(int x, int y) {
this.x = x;
this.y = y;
// name initialization removed
} protected String makeName() {
return "[" + x + "," + y + "]";
}
// Lazily computers and caches name on first use
public final synchronized String toString() {
if (name == null)
name = makeName();
return name;
}
}

6、这是一个奇怪的谜题

  代码:

public class Creator {
public static void main(String[] args) {
for (int i = 0; i < 100; i++)
Creature creature = new Creature();
System.out.println(Creature.numCreated());
}
} class Creature {
private static long numCreated = 0;
public Creature() {
numCreated++;
}
public static long numCreated() {
return numCreated;
}
}

这样不能通过编译,因为,它违背了一个Java规范。Java语言规范不允许一个本地变量声明语句作为一条语句在for、while或do循环中重复执行[JLS 14.12-14]。一个本地变量声明作为一条语句只能直接出现在一个语句块中。(一个语句块是由一对花括号以及包含在这对花括展中的语句和声明构成的。)所以,如果将这个循环改为:

  for(int i=0;i<100;i++){

     Creature creature = new Creature();
  }

加一个大括弧之后,他就能正常编译运行。

Java——类谜题的更多相关文章

  1. 如何用Java类配置Spring MVC(不通过web.xml和XML方式)

    DispatcherServlet是Spring MVC的核心,按照传统方式, 需要把它配置到web.xml中. 我个人比较不喜欢XML配置方式, XML看起来太累, 冗长繁琐. 还好借助于Servl ...

  2. jvm系列(一):java类的加载机制

    java类的加载机制 1.什么是类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装 ...

  3. java类与实例

    最近在看设计模式,感觉自己对java的三大特性的理解不够清晰,搞不清楚抽象类.接口.泛型的用处和优缺点.设计模式学了一半,想着还是停下来脑补一下java的基础,就从java对象开始吧. 一.java对 ...

  4. oracle调用JAVA类的方法

    导入jar包 在oracle中导入需要的jar包,我们把编辑好的java类打成jar包,直接在oarcle里面写简单的调用就可以了,  1.操作系统需要拥有支持loadjava命令的jdk.  2.加 ...

  5. Java 类的实例变量初始化的过程 静态块、非静态块、构造函数的加载顺序

    先看一道Java面试题: public class Baset { private String baseName = "base"; // 构造方法 public Baset() ...

  6. hibernate中java类的成员变量类型如何映射到SQL中的数据类型变化

    hibernate映射文件??.hbm.xml配置映射元素详解--Hibernate映射类型 在从Hibernate的java的成员类型映射到SQL中的数据类型,其内映射方式它满足,SQL可以自己调制 ...

  7. kettle系列-[KettleUtil]kettle插件,类似kettle的自定义java类控件

    该kettle插件功能类似kettle现有的定义java类插件,自定java类插件主要是支持在kettle中直接编写java代码实现自定特殊功能,而本控件主要是将自定义代码转移到jar包,就是说自定义 ...

  8. Myeclipse中导入项目后java类中汉字注释出现乱码问题(已解决)

    今天重装系统,安装了新的Myeclipse后,导入之前的项目后,,出现了乱码问题.乱码问题主要是java类中的注释,而jsp页面中汉字却完好如初: 右键项目,查看项目的编码格式,UTF-8,把java ...

  9. Java类初始化

    Java类初始化 成员变量的初始化和构造器 如果类的成员变量在定义时没有进行显示的初始化赋值,Java会给每个成员变量一个默认值 对于  char.short.byte.int.long.float. ...

随机推荐

  1. ubuntu 设置vpn

    百度了资料 http://jingyan.baidu.com/article/fa4125aca7f1b628ad709271.html 1. 设置 VPN CONNECTION 2.configur ...

  2. Bookmark

    http://stackoverflow.com/https://www.baidu.com/?tn=06074089_27_pghttp://apistore.baidu.com/http://to ...

  3. Oracle中清除BIN$开头的垃圾表的解决办法

    10g的新特性flashback闪回区 在10g中bin开头表示已经删除的放在回收站的表,oracle在删除表时并没有彻底的删除,而是把表放入回收站!purge recyclebin清空回收站即可. ...

  4. JdbcTemplate学习笔记

    JdbcTemplate学习笔记 1.使用JdbcTemplate的execute()方法执行SQL语句 Java 代码 jdbcTemplate.execute("CREATE TABLE ...

  5. 用for、while、do-while循环输出10句“好好学习,天天向上!”

    #include "stdio.h" void main() { int time; ;time<=;time++) printf("%d.好好学习,天天向上!\n ...

  6. lucene索引合并与增量索引

    利用 Lucene,在创建索引的工程中你可以充分利用机器的硬件资源来提高索引的效率.当你需要索引大量的文件时,你会注意到索引过程的瓶颈是在往磁盘上写索引文件的过程中.为了解决这个问题, Lucene ...

  7. 转:LoadRunner获取毫秒及字符串替换实现

    今天做一个性能测试,参数化要求创建用户名不可以重复,想来想不没有什么好的办法来避免用户名字的重复.所以就想用时间+随机数来实现,但是实现中遇到一个问题. 名字中不可以包含.这个特殊的字符的.所以要处理 ...

  8. Dev之ChartControl控件(一)

    ChartControl控件主要包括Chart Title,Legend,Annotations,Diagram,Series五部分:如图: 1.  用RangeControl控件控制ChartCon ...

  9. RadioGroup+TabHost

    =.= //MainActivity public class MainActivity extends TabActivity implements OnCheckedChangeListener ...

  10. android使用百度app分享,app统计出现的异常,FrontiaApplication类

    想在app里加入百度分享和百度统计.查看了百度移动统计的文档后下载官网给的demo.参照demo给出的代码给自己的app加入代码.以上步骤比较简单.不细说,下面说下此过程需要注意的一些点,不然会引发异 ...