Java解惑

来源:互联网 发布:图像拼接的改进算法 编辑:程序博客网 时间:2024/04/30 00:32

一. 表达式之谜


二. 字符之谜


三. 循环之谜

谜题24

byte是有符号的,范围是-128 - 127。而0x90是int类型。比较的时候,不相等。
如果想让其相等,需要进行类型转换:(byte & 0xff) 或者 (byte)0x99。

谜题25

自增运算符对循环的影响。j = j++,先赋值。

谜题26

 Integer.MAX_VALUE加一之后会变成Integer.MIN_VALUE,这对循环会有影响。
可以考虑使用long来表示i变量,或者使用效率更高的i != Integer.MAX_VALUE。

谜题27

(-1 << 32)的结果是-1而不是0。
对于int型的数据移位运算总是取右端数转换成2进制的低五位。
对于long型的数据移位运算总是取右端数转换成2进制的低六位。
32取低五位是0,因此结果是-1。
如果右边的数是负数,同样保留低五(六)位,将其转换为正数。

谜题28

while(i == i + 1) {
}

当浮点数i足够大时候, i = i + 1。

相邻浮点数之间的距离被称为ulp(unit int the last place),一旦ulp超过2,加一将不会产生任何影响。

二进制浮点数算术只是一种近似。

谜题29

while(i != i) {
}
IEEE 754浮点算术保留了一个特殊的值:NaN(not a number)。
double i = 0.0 / 0.0,double i = Double.NaN,
任何浮点数操作,只要一个或多个操作数为NaN,那么结果是NaN。
NaN不等于任何浮点数,包括它自身。NaN  != NaN 。

谜题30

while(i != i + 0) {
}
i != i + 0,如果i是String类型。此时的+就不是算术加号了,被重载为字符串的连接。

谜题31

while(i != 0) {
i  >>>= 1;
}
还是在讲类型转换。
当i为short类型的-1的时候,i >>>= 1,仍旧是-1:首先将short转换为int,然后在截取低位16。
short,byte的任何负数最终都会转换为-1。而char不会,char是无符号的。

谜题32

Integer i = new Integer(0);

Integer j = new Integer(0);

while(i <= j && i >= j && i != j){

}

包装类,当使用==运算符时候,并不是判断值相等,而是引用(兼容性考虑)。<=或者>= 都是值判断。

谜题33

while(i != 0 && i == -i) {
}
整数的边界问题
0x8000 0000 表示最小的负数(Integer.MIN_VALUE),对其求相反数,仍是其本身。

谜题34

final int START = 2000000000;int count = 0;for (float f = START; f < START + 65; f++) {count++;System.out.println(START + 65);}

浮点数精度问题
START+50与START相等,当使用浮点数表示的时候。50比START的ulp距离(128)的一半还小。当超过ulp的距离的一半的时候,无限循环。

谜题35

取余和乘除具有相同的优先级。

四. 异常之谜

谜题36

finally语句中的return语句会覆盖掉try语句中的。

谜题37

该部分还需要进一步理解
一个方法可以抛出的被检查异常集合是它所适用的所有类型声明要抛出的被检查集合的交集。


五. 类之谜

谜题46

函数重载的问题。
JAVA重载解析过程:1. 选取所有可用的方法或者构造器;2. 从过程1中选取的方法或构造器中选择最精确的。
一般而言:可以强制要求编译器选择一个精确的重载版本,将实参转型为形参所声明的类型。

谜题47

继承中静态域的问题。
静态域由声明它的类及其所有子类共享。
如果需要让每一个子类都具有某个域的单独拷贝,必须在每一个子类中声明一个单独的静态域。
如果每一个实例都需要一个单独的拷贝,则可以在基类中声明一个非静态域。

谜题48

静态方法的问题。
对静态方法的调用不存在任何动态分配机制。静态方法是编译时刻选定的,根据修饰符编译期类型选定的。
静态方法不能被覆写,只能被隐藏。
尽量使用类名来调用静态方法。

谜题49

public class Elvis {public static final Elvis INSTANCE = new Elvis();private final int beltSize;private static final int YEAR = Calendar.getInstance().get(Calendar.YEAR);private Elvis() {beltSize = YEAR - 1930;}public int beltSize() {return beltSize;}public static void main(String[] args) {System.out.println(INSTANCE.beltSize());}}
初始化的问题。
final类型的静态域被初始化之前存在被读取的可能,此时该静态域只是所属类型的缺省值。
final类型的域只有在初始化表达式是常量表达式时,才是常量。
类的初始化循环有待进一步理解。

谜题50

instanceof的问题
当instanceof左操作数为null时,返回false。
当instanceof左右操作数都是类的时候,其中一个必须是另一个的子类型,否则编译错误。
转型操作符的行为与instanceof相同,当转型操作的两种类型都是类时,必须其中一个是另一个子类型。

谜题51

class Point {protected final int x, y;private final String name;Point(int x, int y) {this.x = x;this.y = y;name = makeName(); // 3. 调用子类的方法。}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); //2. 转向父类的构造函数。this.color = color; // 5. 初始化该属性。}protected String makeName() {//4.在子类的构造函数结束前执行。return super.makeName() + ":" + color;}public static void main(String[] args) {//1. 调用子类的构造函数。System.out.println(new ColorPoint(1, 2 , "red"));}}
构造函数调用了子类覆写的方法。
实例初始化循环,可以采用惰性初始化。

谜题52

class Cache {static {initializedIfNecessary();}private static int sum;public static int getSum() {initializedIfNecessary();return sum;}private static boolean initialized = false;private static synchronized void initializedIfNecessary() {if (! initialized) {for (int i = 0; i < 100; i++) {sum += i;}initialized = true;}}}public class Client {public static void main(String[] args) {System.out.println(Cache.getSum());}}
同时使用了惰性初始化和积极初始化。
初始化顺序对结果的影响。
修改后的程序
class Cache {private static final  int sum = computeSum();private static int computeSum() {int result = 0;for (int i = 0; i < 100; i++) {result += i;}return result;}public static int getSum() {return sum;}}

谜题53

讲述了私有构造器捕获惯用法。
<span style="font-size:18px;">class Thing {public Thing(int i) {System.out.println("Thing:" + i);}}public class MyThing extends Thing {@SuppressWarnings("unused")private final int arg;public MyThing() {this(1);//可以调用其他类的方法获取返回值。}private MyThing(int i) {super(i);arg = i;}}</span>

谜题54

静态方法调用时,实例不起作用。静态方法是属于类的。

谜题55

java语言规范不允许一个本地变量声明语句作为一条语句在循环中重复执行。一个本地变量声明作为一条语句,只能直接出现在一个语句块中(花括号中的)。
for (int i = 0; i < 10; i++)
String str = "123";

这个编译不通过,修改正确为:

for (int i = 0; i < 10; i++) {
Stirng str = "123";
}

六. 库之谜

谜题56

BigInteger、BigDecimal以及包装类型的实例是不可改变。
BigInteger five = new BigInteger("5");
BigInteger total = BigInteger.ZERO;
total.add(five);  //这并不会改变total的值,调用该方法的返回值,才是加法得到的结果。 
total = total.add(five); // 才是希望得到的结果

谜题57

HashSet中的元素,首先计算hashCode,然后在调用equals()方法。
一个类如果重写了equals()方法,而没有重写hashCode()方法,对HashSet会有影响。
因此两个方法尽量一起重写。

谜题58

本来应该重写equals(Object o),但是重载了,写成了:equals(Class class)
因此在重写的时候尽量使用:@Override

谜题59

主要讲述了:在数字前面加个0就是八进制的数了。

谜题60

一行编码解决问题,主要讲述是四个知识点。
1. LinkedHashSet:维持来的顺序,且没有重复的。
2. 正则表达式在split中的应用。
3. Arrays.deepToString的使用
4. 包装类的位操作:Integer.bitCount();

谜题61

主要讲了Calendar和Data使用的问题。使用时候要注意查API文档。

谜题62

HashMap存储String时候的问题。
HashMap比较键时,使用的是引用等价性而不是值等价性。
语言规范字符串是内存限定的,相等的String常量也是相同的。

谜题63

不要将构造器声明变成方法声明:添加返回值。

谜题64

整数边界的问题
Math.abs(Integer.MIN_VALUE) 仍是其本身
PS:负数取余的问题

谜题65

整数相减越界的问题。
两个整数相减会导致越界,compare时最好不要使用两数相减,改为
public int compare(Integer i1, integer i2) {      return (i2 < i1 ? -1 : (i2 == i1) ? 0 : 1);}

七. 更多类之谜

谜题66

继承的问题。
对于实例方法:命名相同时,子类会覆写父类的方法,且访问权限至少和父类一样大。
对于域:命名相同时,子类会隐藏父类的域,且访问权限任意。

谜题67

不要重用库中的类名。

谜题68

命名的问题。
类名应该以大写字母开头,形式为:MixedCase。
变量以小写字母开头,形式为:mixedCase。
常量以大写字母开头,形式为:MIXED_CASE。
单个大写字母,只能用于类型参数,形式为:Map<K, V>。
包名应该都是小写,形式为:lower.case。
当一个变量和一个类型具有相同的名字,并且作用于相同的作用域时,变量名具有优先权。
即:变量名遮掩(obscure)类型名。同样变量名和类型名可以遮掩包名。
class X {static class Y {static String Z = "Black";}static C Y = new C();}class C {String Z = "White";}public class ShadesOfGray {public static void main(String[] args) {System.out.println(X.Y.Z); //输出结果是:White}}

谜题69

如何引用上题中的被遮掩的类型名。
技巧在某一种特殊的语法上下文环境中使用该名字,语法上下文环境允许出现一个类型名但是不允许出现变量。
技巧一
System.out.println(((X.Y)null).Z);
技巧二
static class Xy extends X.Y{};System.out.println(Xy.Z);
技巧三: 5.0版本之后
public static <T extends X.Y> void main(String[] args) {       System.out.println(T.Z);}

谜题70

具有包内访问权限修饰的方法的类,在其他包内被继承时,该方法不可能被覆写。
即使是方法名相同,也是两个不同的方法。

谜题71

名字重用导致遮蔽(shade):一个声明只能遮蔽类型相同的另一个声明。
静态导入Arrays.toString,当在类中使用toString()时,会调用Object类中的toString方法。

谜题72

名字重用的问题。
final修饰符
对于方法:实例方法不能覆写,静态方法不能隐藏。
对于域:不能被赋值超过一次。因此可以被隐藏。

谜题73

遮蔽:被遮蔽类型可以来自外围类,而不是来自库。
隐藏:本来调用的父类的变量,结果声明了一个同名的私有的变量。

谜题74

重载方法要确定所有的重载版本一致。

谜题75

版本升级后带来的不一致,了解即可。

名字重用术语表

覆写(override)

一个实例方法可以覆写(override)在其超类中可访问大的具有相同签名的所有实例方法,从而能动态分派,,VM将基于实例运行期类型来选择要调用的覆写方法。
class Base {     public void f(){}}class Derived extends Base {    public void f() {} //override Base.f()}

重载(overload)

同一个类中,方法名相同,签名不同。重载的方法是编译期选定的
class A {     public void f (int i){}     public void f (String s)  {}}

隐藏(hide)

子类中的一个域、静态方法或成员类型可以隐藏超类中可访问到具有同名的所有与、静态方法或成员类型。
隐藏一个成员将阻止其被继承。
class Base {     public static void f() {}}class Derived extends Base {     public static void f() {}}

遮蔽(shadow)

变量、方法或类型可以遮蔽文本范围内具有相同名字的所有变量、方法、或类型。
class WhoKnows {      static Stirng sentence = "I do not know";        public static void main(String [] args) {              String sentence = "i know"; //shadow static field               system.out.println(sentence);         }}
遮掩(obscure)
变量可以遮掩同一范围内相同名字的类型,
变量或者类型可以遮掩一个包。
public class Obscure {    static String System; //Obscures type java.lang.System    public static void main(String[] args) {     System.out.println("hello"); //won't compile    }}

八. 更多库之谜

谜题76

将线程的启动方法start(),写成了run();
PS:管程(monitor)锁有待进一步理解。

谜题77

线程中锁的问题。
理解不深刻。

谜题78

反射会造成访问其他包中的非公共类型的成员,引起运行期异常。

谜题79

遮蔽:Thread.sleep()方法遮蔽了自定的方法。

谜题80

反射:如何实例化非静态内部类以及静态内部类。

谜题81

System.out.write():不会自动刷新。需要使用System.out.flush()来刷新。

谜题82

Process使用的问题,没看懂。

谜题83

单例模式与序列化之间的关系。
public class Dog extends Exception {private static final long serialVersionUID = 1L;public static final Dog INSTANCE = new Dog();private Dog() {}public String toString() {return "Woof";}private Object readResolve() {return INSTANCE;}}
Exception实现了Serializable,必须有一个readResolve方法,用以返回它的唯一的实例。
否则可以用反序列化,获取多个对象。

谜题84

Thread.interrupted()方法,清除当前线程的中断状态。
Thread.isInterrupted()方法,只是查询中断状态。

谜题85

在类的初始化期间等待某个后台线程很可能会造成死锁。
public class Lazy {private static boolean initialized = false;static {Thread t = new Thread(new Runnable() {public void run() {initialized = true;}});t.start();try {t.join();} catch (InterruptedException e) {e.printStackTrace();}}public static void main(String[] args) {System.out.println(initialized);}}


1. 主线程调用Lazy.main方法时,检查Lazy类是否已经被初始化,此时没有,所以主线程会记录下当前正在进行初始化,并对这个类进行初始化。
2. 主线程将initialized的值设为false,创建后台线程,该改后台线程将initialized设为true,此时主线程会等待后台线程执行完毕。
3. 但是当后台线程调用run方法,将Lazy.initialized设为true之前,也会检查Lazy类是否已经被初始化。
这个类正在被主线程进行初始化。后台线程会等待,直到初始化完成。
4. 主线程也在等待后台线程结束。造成死锁。

九. 高级谜题



0 0
原创粉丝点击