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标识。

    ---持续更新---

  • 相关阅读:
    linux 解压tgz 文件指令
    shell 脚本没有执行权限 报错 bash: ./myshell.sh: Permission denied
    linux 启动solr 报错 Your Max Processes Limit is currently 31202. It should be set to 65000 to avoid operational disruption.
    远程查询批量导入数据
    修改 MZTreeView 赋权节点父节点选中子节点自动选中的问题
    关于乱码的问题解决记录
    我的网站优化之路
    对设计及重构的一点反思
    我的五年岁月
    奔三的路上
  • 原文地址:https://www.cnblogs.com/xinxinBlog/p/10023713.html
Copyright © 2011-2022 走看看