zoukankan      html  css  js  c++  java
  • Java学习笔记--Thinking in Java(精华部分提取)

    Chapter1 对象导论

    (1)编程即抽象

    (2)每个对象都有接口

    (3)每个对象都提供服务
    设计过程中,应遵循"高内聚,低耦合"原则

    (4)被隐藏的具体实现
    类设计过程,应通过封装的手段,将实现细节隐藏(private),而将接口暴露(public)

    (5)复用具体实现
    复用:同一个实现,应尽可能保证在不同的场景下可以直接使用
    复用的手段包括:组合(composition)、继承(aggregation)..
    所谓组合,即has-a;所谓继承,即is-a

    (6)继承
    基类(父类),通过继承的方式,得到导出类(子类)。子类成为父类的泛化。
    子类可以复制父类所有行为,也可以重写父类的行为。
    理想的继承应为is-a,但实际上通常是is-like-a

    (7)伴随多态的可互换对象
    面向接口编程,在需要改动时(更换接口对象),将修改开销降到最小。
    实现相同接口的类(继承自相同父类的子类),由于多态的作用,对于相同的接口,表现出各自不同的行为。
    多态的实现方法,即后期绑定。在编译期间,编译器无法确定接口方法调用的哪个具体实现。
    将子类转化为父类对象(实现转化为接口)的行为,称为向上转型(upcast);反之,称为向下转型。

    (8)单根继承结构
    java中,所有对象(基本类型不算)具有相同的基类(Object),且所有子类都只能继承自唯一一个父类。

    (9)容器
    jdk中提供了很多不同类型的容器,应对不同场景的使用。

    (10)对象的创建及生命期
    不同于c++,Java中申请的对象不需要手动释放,而由GC释放。常见算法包括:引用计数、根节点可及。
    由于GC,Coder可将精力放在更顶层的设计上。

    (11)异常处理:处理错误
    程序中意外情况,都以异常的形式抛出,供处理。

    (12)并发编程
    多线程/多进程可以提升CPU的利用率,也解决了多个任务需要同时运行的需求,但是也带来了难度和问题。
    隐患主要在于共享资源的访问及修改,而,成为解决并发编程的一大利器。

    (13)Java与Internet
    网络即socket。万维网将遍布世界的不同主机连接起来,提供通信。常见的通信架构:C/S,B/S。

    Chapter2 一切都是对象

    (1)用引用操作对象
    所有的对象都被jvm放在堆中,程序中操作的是对对象的引用。

    (2)必须有你创建所有对象

    jvm内存中存储数据的位置包括:
    a.寄存器,在处理器内部,速度快,但是数量有限
    b.堆栈,方法执行过程中存储局部变量的地点。方法执行结束后,堆栈中的数据自动回收。地址从高向低生长。
    c.,存储对象的内存区域,由GC进行对象的回收管理。地址从低向高生长。
    d.常量存储,常量通常直接存放在代码内部。
    e.非RAM存储。以上几条皆在RAM中,为运行过程中的数据存储,易失。ROM可持久存储。

    基本类型:
    java中,每个基本类型都有一个包装类与之对应。例如,Boolean类包装了boolean基本类型,Character类包装了char基本类型...
    除了基本类型,Java还提供了特殊类型BigInteger/BigDecimal存储高精度数字

    数组:
    java中的数组提供了范围检查,对于越界问题在运行时进行了检查。在创建数组过程,java自动将数组全部置零初始化。

    (3)永远不需要销毁对象
    GC对于已经不再使用的对象,自行回收,不需要编程主动释放。

    (4)类的成员
    类由字段的方法组成,基本成员中的基本类型会被java自动置为默认值(0,false),对象被置为null

    (6)构建一个Java程序
    命名:程序通过包区分不同的命名空间,import关键字可导入包。

    (7)java程序
    javac用于编译java程序,java用于运行java程序(前提是配置好JDK)

    (8)注释和嵌入式文档
    java代码中通过特殊的语法编辑注释,可通过工具javadoc将注释生成文档或HTML。
    例如标签: @see/@author/@param等

    (9)编码风格
    通常使用驼峰风格

    Chapter3 操作符

    (1)int与Integer
    同样表示整形数字,前者为基本类型,后者为类(引用类型)。使用存在区别,在方法内部申请二者,前者存于堆中,后者于栈
    例如,Integer i1=5; Integer i2=5; (i1==i2)结果为false,因为二者虽然数值相同,但是属于不同对象。
    而int j1=5; int j2=5.则有(j1==j2)为true

    (2)指数的表示
    数学中,e表示自然对数的基数(约为2.718)。而在设计FORTRAN语言时,设计师自然的决定用e代表10的幂次,于是设计C/C++/java时沿用下来。
    例如,float f4 = 2e-20f; 代表2*(10的-20次方)

    (3)移位操作符
    包括有符号的移位符<<和>>(符号扩展,右移时,若为正数,高位补0;若为负数,高位补1)
    以及无符号的移位符<<<和>>>(0扩展,右移时,高位补0)

    (4)类型转换
    包括窄化转换和扩展转换,前者指新的类型相比原先容纳更少的数据,后者则转换后容纳更多。
    例如,short转化为int为扩展转换,反之为窄化转换

    (5)整形数据类型的数值运算
    a.若类型比int小(如short、char、byte),会先转换为int在做运算。
    b.不同数据类型做运算,最大的数据类型决定了结果值的类型。

    Chapter4 控制执行流程

    (1)for语句的使用
    for(A;B;C){;}
    A/C中皆可有多个语句,用逗号分隔。例如,
    for(int i=1,j=i+10;  i<5;  i++, j=i*2)
    {
      System.out.println("i="+i+",j="+j);
    }

    (2)swith-case语句
    选择因子类型必须为整数或枚举类型

    Chapter5 初始化与清理

    (1)类可包含多个构造方法Constructor,默认为无参构造器。带参构造器中可调用其他构造器(最多一个)

    (2)清理:终结处理与垃圾回收
    finalize函数在垃圾回收之前调用,用于处理一些无法被GC释放的资源(例如socket、stream、内存相关资源等)。
    在finalize之后,若依然满足垃圾回收条件,则在下次垃圾回收时,将对象的内存资源释放掉。

    (3)垃圾回收工作方式

    (3.1)引用计数,是一种简单但是很慢的垃圾回收计数。每个对象都有一个引用计数,当有引用连接至对象时,引用计数加1;当引用离开作用域或置为null时,引用减1.
    虽然管理引用计数开销不大,但是此过程在程序生命周期中持续发生。垃圾回收期会在所有对象上扫描,当引用数为0时释放。
    此垃圾回收机制存在问题:循环引用时,无法正常进行垃圾回收。而定位该问题开销极大,因此尚未被任何java虚拟机采用。

    (3.2)常用垃圾搜寻及处理方式

    (3.2.1)垃圾搜寻

    在更快的模式中,基于思想:对任何活的对象,一定能最终追溯到其活在堆栈或静态存储区的引用。如果从堆栈或者静态存储区开始,遍历所有引用,就能找到所有活的对象。

    (3.2.2)垃圾处理

    Java虚拟机将采用一种自适应的垃圾回收技术。至于如何处理找到的存活对象,取决于jvm实现。

    方式一:
    有一种方法叫"停止-复制(stop-and-copy)",即暂停程序,将当前存活对象从当前堆复制到另外一个堆,未被复制的就是垃圾。复制到新堆之后,对象时连续紧凑挨着的。搬移之后,所有对象的引用将被修正。
    此种方法,称为“复制式回收器”,效率会降低:首先,需要两个堆,需要多一倍的空间;其次,在于复制,若程序进入稳定器不产生垃圾,来回复制则十分浪费。

    方式二:
    为避免以上情况,一些jvm会进行检查:若未产生新垃圾,则转换工作模式(自适应)为"标记-清扫(mark-and-sweep)"。在垃圾比较少时,此种方式就很快了。

    (3.2.3)jvm中,内存以"块"进行划分,每个块有相应的代数(generation count)。若有块,在停止-复制模式下,垃圾回收器可以将对象往废弃的块里拷贝对象。若块被引用,则其代数会增加。
    垃圾回收器将对上次回收动作之后新分配块进行整理。这对处理大量短命临时对象很有帮助。jvm会进行监视,若对象都很稳定,则会切换至mark-and-sweep模式;若内存碎片过多,则会切换至"stop-and-copy",这就是自适应技术。
    称为"自适应的、分代的、停止复制、标记清扫"式垃圾回收器

    (4)成员初始化

    初始化成员变量时,注意不可向前引用为初始化的变量。例如,下面是不对的:
    class A
    {
      int i = a; //此时a尚未初始化
      int a = 2;
    }

    类的初始化顺序如下:

    在首次调用类的静态方法、静态成员、主动加载类 或 执行构造类的实例时,其内部的静态成员被初始化(有且只有一次):
    先按照从上到下的顺序初始化静态变量,然后执行静态块;
    在实例化某个类时,其内部普通全局变量被初始化:
    先按照从上到下顺序初始化全局变量,然后执行构造方法。

    (5)Java中的数组

    不同于C中的数组,Java中的数组可以在运行时检查到越界行为。因为在Java数组设计时,为其固定分配了一个固有成员:length
    运行时,java自动检查length与访问index的大小关系,判断是否出现越界,若是,则抛出异常。
    Java中的数组,在构造之后,会被自动初始化为空值。
    若想打印数组,可以执行Arrays.toString(a);

    (6)可变参数列表

    所谓可变参数列表,语法如下:
    void f(String... strArray){;}
    其本质是一个数组,可用for(String arg : strArray); 的方式访问其内容。

    (7)方法重载下的访问选择

    若存在方法重载(多个方法名称一致,参数不同),优先按照数据类型完全一致的匹配调用;
    若不存在一致的,java会利用自动包装机制寻找相近的方法调用;

    Chapter6 访问权限控制

    (1)包

    包(package),将不同生产商的代码分离开来。通过import,可将包导入。
    若要访问jar文件,需要将其完整路径放在CLASSPATH中。执行import时,将从CLASSPATH下查找相应class文件。
    包的名称应与路径相对应,例如:com.google.vector 应在com/google/vector.class
    通过import,可实现类似C语言中的条件编译功能。

    (2)访问权限修饰词

    相同路径下未指明包名的类,默认在相同的默认包中;
    class未加任何权限修饰词,默认为包访问权限(同包可访问);

    (3)类的访问权限

    权限的设定有以下限制:
    a.类中的方法访问权限 <= 类的访问权限。
    b.每个编译单元(文件)都只能有一个public类。
    c.public类的名称必须与编译单元的文件名相匹配。

    Chapter7 复用类

    (1)继承

    通过继承父类得到的子类,可以复用父类所有的public/protected接口。

    a.继承的本质:
    子类实例化得到的对象中,其内部隐藏了一个基类的子对象(包装在内部)。

    b.子类的初始化
    子类在执行构造方法时,若子类内部未主动调用父类的构造方法,则java会使其自动调用父类的默认构造方法。
    从而可以保证,任何时候,基类对象都要先于子类对象创建。
    此时,父类和子类的初始化顺序为:
    父类静态全局变量-->父类静态块-->父类普通全局变量-->父类构造方法-->
    子类静态全局变量-->子类静态块-->子类普通全局变量-->子类构造方法

    (2)代理
    代理是对象之间除了组合与继承之外的第三种关系。
    代理:将A类的对象放在B类中,且让B对外暴露于A相同的接口,则B即A的代理。
    通过代理,可实现一些额外功能的封装:例如远程调用的代理(RPC),大型对象的创建(网络图片的加载代理),访问代理(控制模块的访问权限)...

    (3)继承下的清理工作
    每个类可实现一个方法dispose(),其内部释放一些必要的外部资源(Socket/FileStream/Handle/other os resources...)。
    与类的构建不同,dispose()清理的顺序与构造顺序完全相反:
    因为后初始化的成员(B)可能对先初始化的成员(A)有依赖,若先释放A,可能会对B产生影响。因此,为了避免这种影响,要先释放后初始化的成员B。即释放与初始化顺序相反。

    (4)Protected关键字
    Java中,protected修饰的成员,为子类和包内可访问。

    (5)final关键字
    final可以修饰字段、方法以及类,详细如下:

    a.final修饰字段,字段具有值不可修改的特性。
    若在字段属于基本类型,且在定义处直接赋固定值,那么属于编译时常量,在运行时不可修改。
    若在定义时为给出赋值,那么在运行时第一次赋值之后,不可再次赋值,属运行时常量。
    需要注意的是,若final字段为对象,那么字段本身不可指向其他对象,但对象内部字段修改不受限制。
    例如 final Animal animal = new Cat();则animal变量不可赋值成其他对象,但是animal对象的字段可以修改(eg. animal.name = "Tom",是没问题的)

    b.final修饰方法,那么方法在子类中不可重写。
    一方面,限制了继承下的方法重写;
    另一方面,可建议编译器将方法转换为内嵌方法,提升方法运行效率。

    c.final修饰类
    final修饰类时,那么类不可再被继承。

    Chapter8 多态

    (1)方法绑定
    将一个方法调用和一个方法主体关联起来被称为绑定。
    若在程序执行前进行绑定,称为前期绑定;为了解决向上转型之后的方法调用问题,解决办法就是后期绑定,即在运行时根据对象的类型进行绑定。
    Java中,除了static和private方法之外,其他方法都是后期绑定。

    缺陷:全局变量与static方法不支持多态,仅非private非静态方法支持多态。
    若在构造方法中调用其他方法,会有多态效果,可能会有意外(子类尚未初始化,调用方法效果存在意外)。

    (2)向上转型与向下转型
    向上转型时,类型安全,但是可能会丢失子类的行为;
    向下转型时,可能会产生类型转换异常;

    Chapter9 接口

    (1)抽象类与抽象方法

    抽象类,是包含抽象方法的类,用abstract修饰。
    而抽象方法,只是一个抽象的方法定义,无具体实现。
    抽象类不可被实例化,子类若非抽象类,需实现抽象父类中的所有抽象方法。
    需要注明的是,抽象类除了包含抽象方法和不能实例化,其他的特性和普通类无异(可以有属性定义,普通方法实现...)。

    (2)接口

    interface关键字定义接口,产生一个完全抽象的类,没有提供任何具体实现。定义的方法默认public。
    接口中可以包含域,但是默认都是static final。
    同一个类,可以实现多个不同的接口,但不能继承自多个类。

    (3)通过继承来扩展接口
    虽然子类不可继承自多个父类,但是接口可以继承自多个父接口,以实现接口的扩展。

    (4)接口的意义
    接口是对类功能的抽象,可以对类的不同功能进行分别设计。同时,面向接口编程可以降低模块间的耦合度,实现模块间的弱耦合,提升代码的可扩展性。


    Chapter10 内部类

    (1)内部类与外部类的关系

    内部类(InnerClass)是定义在一个类内部的类(EnclosedClass), 创建内部类时,需要确保有对应外部类示例与之关联。
    eg.

    class Outer
    {
    	Inner inner = new Inner();
    		
    	class Inner
    	{
    	}
    }

    在Outer内部,可以随意创建Inner实例。而在外部调用,需按如下语法:

    Outer outerInstance = new Outer();
    Outer.Inner inner = onterInstance.new Inner();

    即,内部类实例化依赖外部类对象,这种语法确保每个内部类Inner实例都一定有外部类Outer实例与之关联。

    而内部类中,可以直接获取到与之关联的外部类实例:

    class Outer
    {
    	Inner inner = new Inner();
    	
    	class Inner
    	{
    		Outer outer = Outer.this;
    	}
    }
    

    即Inner中通过Outer.this,即可获取到与之关联的外部类实例。

    内部类中,不能包含static成员与数据。

    (2)内部类与外部类之间的访问权限

    内部类中,可以访问到外部类的成员(因为内部类在外部类内部)。
    依据内部类种类,内部类可以获取到外部类中的不同内容:

    a. 普通内部类

    class Outer
    {
    	public int a;
    	protected int b;
    	private int c;
    	int d;
    	class Inner
    	{
    		void print()
    		{
    			System.out.println(a);
    			System.out.println(b);
    			System.out.println(c);
    			System.out.println(d);
    		}
    	}
    }
    

    普通内部类中,可以访问到Outer中所有全局变量。

    b.局部内部类

    class Outer
    {
    	public int a;
    	protected int b;
    	private int c;
    	int d;
    	
    	void check()
    	{
    		int f=0;
    		class Inner
    		{
    			void print()
    			{
    				System.out.println(a);
    				System.out.println(b);
    				System.out.println(c);
    				System.out.println(d);
    				System.out.println(f);
    			}
    		}
    	}
    }	
    

    局部内部类中,除了可以访问到Outer的全局变量,同时还可访问所在局部方法中相应的变量。

    c.匿名内部类

    class Tmp
    {
    	void tmp();
    }
    class Outer
    {
    	void check()
    	{
    		return new Tmp()
    		{
    			void tmp(){...}
    		};
    	}
    }
    

    匿名内部类对外部类的成员访问权限,类似以上两种。

    (3)嵌套类

    内部类定义中加上static,即是嵌套类。
    嵌套类与外部类之间无实例关联关系,可以在外部直接: Nested nest = new Outer.Nested();

    嵌套类可专门用来写测试方法:

    class Outer
    {
    	void check(){;}
    	static class Nested
    	{
    		public static void main(String[] args)
    		{
    			Outer outer = new Outer();
    			outer.check();
    		}
    	}
    }
    

    嵌套类的编译class文件专门用Outer$Nested.class标识。

    ---持续更新---

  • 相关阅读:
    Windows Forms中通过自定义组件实现统一的数据验证(二)
    The WindowsClient.NET Community Site Launches
    二十六岁,仍在路上
    Visual Studio 2008 Express版本下载
    Page Controller及其在ASP.NET中的实现
    iBATIS In Action:使用映射语句(二)
    在VS2005中创建项目模板来提高开发效率
    2007年,听见春天的脚步
    iBATIS In Action:使用映射语句(一)
    iBATIS In Action:序言和目录
  • 原文地址:https://www.cnblogs.com/xinxinBlog/p/10023713.html
Copyright © 2011-2022 走看看