• Java解惑


    一. 表达式之谜


    二. 字符之谜


    三. 循环之谜

    谜题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. 主线程也在等待后台线程结束。造成死锁。

    九. 高级谜题



  • 相关阅读:
    Linux编辑器- vi / vim
    Java使用POI对Excel进行基本操作(4)-Excel中绘制图片
    Java使用POI对Excel进行基本操作(3)-合并单元格
    Java使用POI对Excel进行基本操作(2)-基本操作和样式设置
    Java使用POI对Excel进行基本操作(1)-概述和maven依赖
    Linux之docker搭建
    docker的个人理解
    python接口自动化-requests-toolbelt处理multipart/form-data
    python3.6安装lxml库
    pytest之assert断言
  • 原文地址:https://www.cnblogs.com/lcchuguo/p/5081678.html
Copyright © 2020-2023  润新知