zoukankan      html  css  js  c++  java
  • Think in Java 第四 五 章

    Think in Java 第四章

    控制执行流程

    测试while

    public class whileTest {
        static boolean condition(){
            boolean result = Math.random() < 0.99;
            System.out.println(result + ",");
            return result;
        }
    
        public static void main(String[] args) {
            while (condition())
                System.out.println("Inside 'while'");
            System.out.println("Exited 'while'");
        }
    }
    ===============================================
        true,
    Inside 'while'
    true,
    Inside 'while'
    true,
    Inside 'while'
    true,
    Inside 'while'
    false,
    Exited 'while'
    

    Math.random 随机生产 0 ---1 的数字

    do - while 至少执行一次

    for循环常用于计数任务

    public class whileTest {
        public static void main(String[] args) {
            Random rand1 = new Random();
            Random rand2 = new Random();
            for(int i = 0; i < 5; i++) {
                int x = rand1.nextInt();
                int y = rand2.nextInt();
                if(x < y) System.out.println(x + " < " + y);
                else if(x > y) System.out.println(x + " > " + y);
                else System.out.println(x + " = " + y);
            }
            Random rand3 = new Random();
            Random rand4 = new Random();
            for(int i = 0; i < 5; i++) {
                int x = rand3.nextInt(10);
                int y = rand4.nextInt(10);
                if(x < y) System.out.println(x + " < " + y);
                else if(x > y) System.out.println(x + " > " + y);
                else System.out.println(x + " = " + y);
            }
        }
    
    }
    ====================================================
    
    -1636716208 < -1366578013
    -630773338 > -1420580444
    342336183 < 784053746
    1142887002 > 325688001
    -1102830792 > -1683393166
    8 > 4
    3 > 0
    7 < 8
    0 < 3
    1 < 6
    

    forseach

    不必创建 int 变量 去对由其访问项构成的序列 进行计数

    for (int i : range(10))
    print(i+"");
    ===============
    0 1 2 3 4 5 6 7 8 9
    

    练习8

    public class SwitchTest {
    	public static void main(String[] args) {
    		for(int i = 0; i < 11; i++) {	
    			switch(i) {
    				case 0: print("zero"); break;
    				case 1: print("isa"); break;
    				case 2: print("dalawa"); break;
    				case 3: print("tatlo"); break;
    				case 4: print("apat"); break;
    				case 5: print("lima"); break;
    				case 6: print("anim"); break;
    				case 7: print("pito"); break;
    				case 8: print("walo"); break;
    				case 9: print("siyam"); break;
    				default: print("default");
    			}
    		}
    	}	
    }
    ======================================================
    zero
    isa
    dalawa
    isa
    dalawa
    dalawa
    tatlo
    apat
    lima
    anim
    pito
    walo
    siyam
    

    练习9

    public class Fibonacci {
    	int fib(int n) {
    		if(n < 2) return 1;
    		return (fib(n - 2) + fib(n - 1));
    	}
    	public static void main(String[] args) {
    		Fibonacci f = new Fibonacci();
    		int k = Integer.parseInt(args[0]);
    		System.out.println("First " + k + " Fibonacci number(s): ");
    		for(int i = 0; i < k; i++)
    			System.out.println(f.fib(i));
    	}	
    }
    ==================================================
    1
    1
    2
    3
    

    第五章

    初始化与清理

    构造器 和 垃圾回收机制

    5.1 用构造器 确保初始化

    ​ 可以假想为每一个类都定义了 initialize() 方法。通过构造器,确保每个类都被初始化了。创建对象时,如果其类具有构造器,Java就会在用户操作之前自动调用相应的构造器,从而保证了初始化。

    构造器 与 类 同名, 方便计算机自动找到,而且不与自定义的属性或者方法重名。

    构造器

    • 名称与类名完全相同,方法名的首字母 小写,不适用于构造器
    • 没有参数的构造器,叫无参构造 或者 默认构造
    • 有形式参数的构造器叫有参构造,以便指向如何创建对象(Tree 类有一个构造器,接受整形变量来表示数的高度,创建Tree对象的时候,就形成了一个有高度的Tree)
    • 有助于 减少错误,代码容易阅读

    练习1

    lass Tester {
    	String s;
    }
    
    public class ConstructorTest {
    	public static void main(String[] args) {
    		Tester t = new Tester();
    		System.out.println(t.s);
    	}
    }
    =============================================
    null
    

    练习2

    class Tester2{
        String s1;
        String s2 = "hello";
        String s3 = "initialization by self";
        Tester2(){
            s3 = "constructor initialization";
        }
    }
    public class ConstructorTest {
        public static void main(String[] args) {
            Tester2 t2 = new Tester2();
            System.out.println("t2.s1="+ t2.s1);
            System.out.println("t2.s2="+ t2.s2);
            System.out.println("t2.s3="+ t2.s3);
        }
    }
    =================================================
    t2.s1=null
    t2.s2=hello
    t2.s3=constructor initnizetion
    

    构造器初始化优先级高

    5.2 方法重载

    名字

    • 创建一个对象时,给对对象分配的存储空间取了一个名字

    • 方法则是给某个动作取个名字

    • 通过名字引用所有的对象和方法。

    • 每一个方法都要有唯一的名称

      ​ 假设你要创建一个类,既可以用标准方法初始化,也可以从文件里读取信息来初始化,这就需要两个构造器:一个默认构造器,一个取字符串作为形式参数----该字符串表示初始化方法对象所需要的文件名称。急类名。为了让方法名相同而形式参数不同的方法同时存在,必须用到方法的重载

    5.2.1 区分方法的重载

    区分方法的重载 其实很简单,

    • 每个重载的方法都必须有第一无二的参数列表。

    • 参数的顺序也可以把他们区分开来

    5.3 默认构造方法

    没有形式参数,创建一个“默认对象”。如果你写的类中没有构造器,则编译器会自动帮你创建一个默认构造方法。

    练习3

    class Kabayo{
        Kabayo(){
            System.out.println(" is a Constructor");
        }
    }
    
    public class DefaultConstructorTest {
        public static void main(String[] args) {
            Kabayo k = new Kabayo();
        }
    }
    ======================================
         is a Constructor
    

    5.4 This 关键字

    如果 只有 一个 Peel 方法,创建了一个对象 a,一个对象 b.

    a.peel(1);

    b.peel(2);

    它是被a调用了 还是 b呢?

    编译器 暗自中 把 所操作对象的引用 作为 参数传递给 peel() 内部表示形式

    peel(a,1);

    peel(b,1);

    在方法内部获得对当前对象的引用, 当前对象 : 调用这个方法的对象。 this

    this 值调用方法的那个对象的引用 a b

    • 当你 在 方法中 调用 同一个类的其他方法 直接调用就好
    • 当你在方法中使用了 this 会应用到同一类的其他方法
    • 当需要返回当前对象的引用时,常常用 return 返回this
    public class leaf {
        int i =0;
        leaf increment(){
            i++;
            return this;
        }
        void print(){
            System.out.println("i = " +i);
        }
    
        public static void main(String[] args) {
            leaf x = new leaf();
            x.increment().increment().increment().print();
        }
    }
    ==========================================================
    3
    

    由于 increment()通过this 关键字返回了对当前对象的引用,所以很容易在一条语句里对同一个对象执行多次操作。

    • this 关键字 对于将当前方法传递给其他方法也很有用

    练习8

    public class Doc {
        public static void main(String[] args) {
            new Doc1().intubate();
        }
    }
    class Doc1{
        void intubate() {
            System.out.println("prepare patient");
            laryngoscopy();
            this.laryngoscopy();
        }
        void laryngoscopy() {
            System.out.println("use laryngoscope");
        }
    
    }
    ===================================================
    prepare patient
    use laryngoscope
    use laryngoscope
    

    5.4.1 在构造器中调用构造器

    一个类中写了多个构造器,有时可能想在一个构造器中调用另一个构造器,避免代码重复 可以用this

    • This 表示 这个对象 或者 当前对象,而它本身代表当前地下的引用。

    • this 添加了参数列表,对符合此参数列表的某个构造器的明确调用

      • 只能调用一个 不能调用两个
      • 编译器 禁止在其他方法里面 调用 构造器

    5.4.2 static的含义

    static 就是没有 this的 方法

    • 在 static 方法中 不能调用非静态方法,反过来可以。
    • 在没有创建任何对象的前提下,仅仅通过类本身来调用static方法。
    • 全局方法。

    5.5 清理 :终极处理 和 垃圾回收

    • 垃圾 回收 只知道释放哪些经由 new分配的内存

    • 为了释放 那些 不由 new 分配的内存,允许在 类中定义 finalize()方法。

      finalize

      1. 垃圾回收 启动 (准备回收对象占用的内存)
      2. 调用 finalize ()方 法 做一些重要的清理工作
      3. 真正回收 内存。
      4. 有趣的用法 终结 条件的验证

    无论对象时如何创建的 ,垃圾回收器都会负责释放对象所占据的所有内存。所以 finalize之所以要用 ,是因为在分配内存时候可能用到了 本地方法 的情况 本地方法目前只支持 c 和 c++

    也许调用了 c 的 malloc()函数 系列 来分配存储空间。而且除非用 free()函数 来释放 空间 否则 得不到释放

    如果JVM 虚拟机 并未 面临内存耗尽的情况 是不会浪费实际去执行 垃圾回收 回复内存的

    练习10

    class WebBank{
        boolean loggedIn = false;
        WebBank(boolean logStatus){
            loggedIn = logStatus;
        }
        void logIn(){
            loggedIn = true;
        }
        void logOut(){
            loggedIn = false;
        }
        protected void finalize(){
            if (loggedIn)
                System.out.println("Error : still logged in");
        }
    }
    public class TerminationConditionEx {
        public static void main(String[] args) {
            WebBank bank2 = new WebBank(true);  // 定义出来不用 垃圾回收自动清理
            bank1.logOut();
            new WebBank(true);  //  一直new 一个 对象 ,一直得不到清理。调用 fianlize方法 检查 验证终结条件
            System.gc();
    
        }
    }
    
    ==========================================================
    Error : still logged in
    

    练习11

    class Webbank {
        boolean loggedIn = false;
        Webbank(boolean logStatus) {
            loggedIn = logStatus;
        }
        void logOut() {
            loggedIn = false;
        }
        protected void finalize() {
            if(loggedIn)
                System.out.println("Error: still logged in");
            // Normally, you'll also call the base-class version:
            // super.finalize();
        }
    }
    public class BankTest {
        public static void main(String[] args) {
            Webbank bank1 = new Webbank(true);
            Webbank bank2 = new Webbank(true);//垃圾回收自动清除
            new Webbank(true);  // 添加未被处理对象 
            // Proper cleanup: log out of bank1 before going home:
            bank1.logOut(); // bank1 对象 被清理
            // Forget to logout of bank2 and unnamed new bank
            // Attempts to finalize any missed banks:
            System.out.println("Try 1: "); 
            System.runFinalization(); 
            System.out.println("Try 2: ");
            Runtime.getRuntime().runFinalization();
            System.out.println("Try 3: ");
            System.gc();
            System.out.println("Try 4: ");
            // using deprecated since 1.1 method:
            System.runFinalizersOnExit(true);
        }
    }
    
    =======================================================================
        
    

    练习12

    class Tank{
        int howFull = 0;
        Tank(){this(0);}
        Tank(int fullness){
            howFull = fullness;
        }
        void sayHowFull(){
            if(howFull == 0) System.out.println("Tank is empty");
            else System.out.println("Tank filling status =" + howFull);
        }
        void empy(){
            howFull = 0;
        }
        protected void finalize(){
            if(howFull == 0)
                System.out.println("Error:Tank is empty");
        }
    
    }
    
    public class TankTest {
        public static void main(String[] args) {
            Tank tank1 = new Tank();
            Tank tank2 = new Tank(3);
            Tank tank3 = new Tank(5);
    
            tank2.empy();
    
            new Tank(6);
            System.out.println("Check tanks:");
            System.out.println("tank1:");
            tank1.sayHowFull();
            System.out.println("tank2:");
            tank2.sayHowFull();
            System.out.println("tank3:");
            tank3.sayHowFull();
            System.out.println("first forced gc():");
            System.gc();
            System.out.println("try deprecated runFinalizersOnExit(true):");
            System.runFinalizersOnExit(true);
            System.out.println("last force gc():");
            System.gc();
        }
    }
    Check tanks:
    tank1:
    Tank is empty
    tank2:
    Tank is empty
    tank3:
    Tank filling status =5
    first forced gc():
    try deprecated runFinalizersOnExit(true):
    last force gc():
    Error:Tank is empty
    Error:Tank is empty
    

    5.5.4 垃圾回收是如何工作

    垃圾回收对与提高对象的创建速度有明显的效果。 存储空间的释放竟然会影响存储空间的分配

    Java中 堆的实现 像一个传送带,每分配一个内存,它就前进一格。 对象在存储空间的分配速度非常快。Java的堆指针只是简单的移动到尚未分配的区域。但不是单纯的像传送带一样,垃圾回收的介入,在分配内存的同时,也释放前面已经分配好的内存,堆指针可以指向释放的区域,使得对象紧凑排列。

    • 引用计数法。常用来说明垃圾收集的工作方式,但似乎从未被应用于任何一种JVM中
    • 对 任何 “活”,一定能最终追溯到其存活在堆栈或者静态存储区之中的引用。

    如何找到活着的对象(不同的Java虚拟机 实现不同)

    • 停止--复制 (不属于后台回收模式)停止程序 将所有存活的对象从当前堆复制到另一个堆,没有完全复制的都是垃圾。当对象复制到新堆的时候是 一个挨着一个
      • 引用必须被修正 (位于 堆 或 静态存储区的引用可以被直接修改)
      • 必须有两个堆 JVM 从堆中按需要 分配几个大堆 复制动作发生在大堆之间
      • 当产生少了垃圾 或者 没有垃圾的时候,一味的复制 会浪费很大的效率
        • 为了避免: JVM 会进行检查:没有新垃圾产生或者少量,切换到另一种模式(标记 -- 清除)
    • 标记--清除 思路也是 : 从堆栈 或者 静态存储区域出发,遍历所有引用,进而找出存活的对象。
      • 步骤
        • 每当找到一个活得对象,就会给对象一个标记,这个过程不会回收任何对象
        • 标记工作全部完成后,清理没有被标记的对象。空间是不连续的 ,重新整理剩下的对象。
    • 自适应
      • JVM , 内存 分配以较大的块为单位。 每个块都有相应的 代数(generation count) 来记录 是否存活,通常 块在某处被引用,代数增加
        • 大型对象单独占一个块。 不会被 复制 只是 代数增加
        • 小型对象 会被 复制 并整合
      • JVM 会 监视
        • 所有的 块 都稳定,但是垃圾回收效率 很低 切换到 标记 -- 清楚 方式
        • 堆中空间出现 很多碎片, 切换到 停止--复制

    JVM 有 许多 附加技术 用来 提升速度

    • 类加载器 有关 的
      • Just in time 即时 把 程序 全部 或者 部分 翻译成 本地机器码(JVM 工作),程序运行速度会得到提高。

    例子

    ​ 当 加载 一个 类库时候(通常时该类创建第一个对象),编译器会找到 .class文件,然后将该类的 字节码 装入内存。

    两种方案:

    • 即时编译器 编译所有代码
      • 降低程序速度
    • 惰性评估 (lazy evaluation)
      • 即时 编译器 只在需要的时候 编译代码 不会被执行的代码 压根不会 被编译。
      • 代码每次 被执行的时候都会做一些 优化 执行的次数 越多 速度越快。

    成员初始化

    Java 尽力 保证: 所有的 成员变量在使用前都能得到恰当的初始化。对于方法的局部变量,Java以编译时的错误来贯彻这种保证

    • 基本数据 类型 成员 保证都会有一个初始值

    5.6.1 指定初始化

    5.7 构造器 初始化

    可以用 构造 来初始化。 无法阻止 自动 初始化 的 进行,他将在构造器被调用之前发生

    5 .7.1 初始化 顺序

    变量 定义的先后顺序 决定了 初始化的先后顺序。即使变量定义散步于方法定义之间,他们仍然会在任何方法(包括构造器)被调用之前得到初始化。

    5.7.2 静态数据的初始化

    ​ 无论建立多少个对象,静态数据都只占一份 存储区域。static 不能应用与局部变量,因此它只能作用 于

    • 静态的基本域 没有进行初始化 , 获得基本类型的标准初值
    • 一个对象的引用,默认值就是null
    • 静态初始化 只有在 必要时刻才会 进行,如果 在类中定义了静态 或者类中的方法定义了静态,那么在没有调用这个类(生成类的对象) 或者方位类的静态成员 那么 不会被初始化,如果 调用了这个类 (创建 类 对象 或者 类的精要方法 静态域 首次被访问)
      • Java解释器必须查找到类路径定位到 类.class文件。
      • 然后载入 class 有关静态初始化的所有操作都会被执行,(因此,静态初始化只在 class对象首次加载的时候进行一次)
      • 当new一个对象的时候,首先会在堆上为这个 对象 分配足够的存储空间
      • 保证这个 空间事清零的 自动的将 类中的所有基本类型 都设置成默认值,数字 字符 布尔是 0 引用为null
      • 执行所有字段定义处的初始化动作
      • 执行构造器。
    • 静态变量 被 初始化 过后 再 重复调用 不会 被出被再次初始化

    5.7.3 显式的静态初始化

    Java 允许 将多个静态初始化 组织 成 一个 “静态块”


    这段代码仅执行一次 :当首次生成 这个类的 一个对象时,或者首次访问 属于那个类的静态数据 成员时

    练习 14

    class Go {
        static String s1 = "run";
        static String s2, s3;
        static {
            s2 = "drive car";
            s3 = "fly plane";
            System.out.println("s2 & s3 initialized");
        }
        static void how() {
            System.out.println(s1 + " or " + s2 + " or " + s3);
        }
        Go() {
            System.out.println("Go()"); }
    }
    
    public class ExplicitStaticEx {
        public static void main(String[] args) {
        	System.out.println(g1.s1);
            System.out.println("Inside main()");
            Go.how();
            System.out.println("Go.s1: " + Go.s1);
        }
        static Go g1 = new Go();
        static Go g2 = new Go();
    }
    ================================================
    s2 & s3 initialized
    Go()
    Go()
    run
    Inside main()
    run or drive car or fly plane
    Go.s1: run
    

    5.7.4 非静态实例化

    Java 中也有 实例初始化 的类似语法, 用来初始化每个对象的非静态变量。

    练习 15

    class Test {
        String s;
        {
            s = "Initializing string in Tester";
            System.out.println(s);
        }
        Test() {
            System.out.println("Tester()");
        }
    }
    
    public class InstanceClauseTest {
        public static void main(String[] args) {
            new Test();
        }
    }
    ====================================================
    Initializing string in Tester
    Tester()
    
    

    5.8 数组初始化

    int[] a

    定义的时候 初始化

    int[] a = new int[rand.nextInt(20)]

    a[i] = rand.nextInt(500);

    也可以用花括号括起来 的 列表 来 初始化 对象数组。 有两种形式

    Integer[] a = {
    
    	new Integer(1),
    
    	new Integer(2),
    
    	3,  // autoboxing自动封装
    
    }
    
    Integer[] b = new Integer[]{
    
    	new Integer(1),
    	new Integer(2),
    	3, //autoboxing
    }
    

    创建一个引用数组, 创建一个对象 并把对象赋值给引用 才算初始化进程结束

    练习16

    public class StringArrays {
    
            public static void main(String[] args) {
                String[] s = { "one", "two", "three", };
                for(int i = 0; i < s.length; i++)
                    System.out.println("s[" + i + "] = " + s[i]);
            }
        }
    

    练习17

    class InitTest {
    	InitTest(String s) {
    		System.out.println("InitTest()");
    		System.out.println(s);
    	}
    }
    
    public class InitTest17 {
    	public static void main(String[] args) {
    		InitTest[] it = new InitTest[10];
    	}
    }
    

    什么都没打印


    练习18

    class InitTest {
        InitTest(String s) {
            System.out.println("InitTest()");
            System.out.println(s);
        }
    }
    
    public class InitTest18 {
        public static void main(String[] args) {
            InitTest[] it = new InitTest[5];
            for(int i = 0; i < it.length; i++)
                it[i] = new InitTest(Integer.toString(i));
        }
    }
    ===========================================================
    InitTest()
    0
    InitTest()
    1
    InitTest()
    2
    InitTest()
    3
    InitTest()
    4
    
    

    5.8.1 可变参数列表

    可以 应用于 参数个数 或者 类型 未知 的 场合。

    由于所有的类都直接或者间接继承object类,所以可以 创建 以 object 数组 为参数的方法。

    **有了 可变参数 编译器 就再也不哟个 显示的编写 数组 语法了,当你 指定参数时,编译器 实际上会为你填充参数 如果你有了 一组事物 你可以把他们当作列表传递 而如果你已经有了一个数组 该方法可以把它们当作 可变参数列表传递 **

    当有 可选的 尾随(trailing) 参数 这一方法 就会很好用

    可变参数 列表为 数组的情况 如果没有 元素 数组尺寸为 0

    可变参数 列表 可以 于 自动包装机制 和谐相处

    可变参数列表 使 重载 变得 复杂了

    每种情况下,编译器都会自动包装机制来匹配重载方法,然后匹配最明确的方法

    在不使用 参数调用 f() 时 编译器 就不知道 调用那个方法了

    可以 增加一个非 可变参数来 解决


    练习19

    public class InitTest19 {
        static void showStrings(String... args) {
            for(String s : args)
                System.out.print(s + " ");
            System.out.println();
        }
        public static void main(String[] args) {
            showStrings("one", "TWO", "three", "four");
            showStrings(new String[]{"1", "2", "3", "4"});
        }
    }
    ===========================================================
    one TWO three four 
    1 2 3 4   
    

    练习 20

    public class VarargEx20 {
        public static void main(String... args) {
            for(String s : args)
                System.out.print(s + " ");
            System.out.println();
        }
    }
    

    5.9 枚举类型

    enum 关键字

    enum 可以和 switch 组合

    练习 21

    public class EnumEx21 {
        public enum Bills {
            ONE, FIVE, TEN, TWENTY, FIFTY, HUNDRED
        }
        public static void main(String[] args) {
            for(Bills b : Bills.values())
                System.out.println(b + ", ordinal " + b.ordinal());
        }
    }
    ===================================================================
    ONE, ordinal 0
    FIVE, ordinal 1
    TEN, ordinal 2
    TWENTY, ordinal 3
    FIFTY, ordinal 4
    HUNDRED, ordinal 5
    

    练习 21

    enum Bills {
        ONE, FIVE, TEN, TWENTY, FIFTY, HUNDRED
    }
    public class Wallet {
        Bills b;
        public static void main(String[] args) {
            for(Bills b : Bills.values()) {
                System.out.print("Worth: ");
                switch(b) {
                    case ONE: System.out.println("$1"); break;
                    case FIVE: System.out.println("$5"); break;
                    case TEN: System.out.println("$10"); break;
                    case TWENTY: System.out.println("$20"); break;
                    case FIFTY: System.out.println("$50"); break;
                    case HUNDRED: System.out.println("$100"); break;
                    default: break;
                }
            }
        }
    }
    =====================================================================
    Worth: $1
    Worth: $5
    Worth: $10
    Worth: $20
    Worth: $50
    Worth: $100
    
  • 相关阅读:
    Hibernate映射Map属性2
    Ajax向Controller发送请求并接受数据需要注意的一个细节
    设置 jsp 表格相邻两行的颜色不一样
    Hibernate映射Map属性
    自己写的爬虫
    在一个jsp页面中引用另一个jsp文件的路径的问题
    state设计原则
    项目图标库怎样选择
    一些有用的插件
    Hook
  • 原文地址:https://www.cnblogs.com/AronJudge/p/14336203.html
Copyright © 2011-2022 走看看