JAVA编程思想读书笔记(1--7章)

本文总结了JAVA编程中的关键知识点,包括类成员默认值、访问控制、单根继承、泛型意义、异常处理等内容,并深入探讨了JAVA语言特性和编程技巧。
学校在大一开设了JAVA的课程,但是学到的内容实在有限。此次进行《JAVA编程思想》的阅读,算是查漏补缺,也是对JAVA各个部分建立更深的理解。把读书过程中的一些细碎但自己没有掌握的知识点整理下来,便于以后复习。

1.所有类的成员基本数据类型字段都有一个默认值,除了char是null外其他都是0.
    局部变量不适用

2.为了标识类库的唯一性,JAVA希望开发者使用自己的域名反转来作为包名,如:com.baidu.myproject。

3.每一个对象都是一个类的实例,包括自己的特性和行为,通过向对象发送消息来通知他调用自己的方法。在新建对象时获得一个引用,通过这个引用发送消息。

4.访问控制有两个理由:(1)不让使用类的程序员触及那些他们不应该触及的实现部分(2)允许改变类的内部工作方法而不影响使用

5.单根继承:JAVA中的所有类都继承自Object,这样做有很多好处,所有对象都可以轻松的在堆上创建,参数传递也很方便,垃圾回收器也容易了很多。垃圾回收器是JAVA很有特色的机制,他可以自动发现对象在何时不再被使用,从而销毁他(比如通过作用域的判断)

6.泛型的意义:如果没有泛型,容器中存放的都是Object,在取出使用时需要向下转型,这样会有风险。但是通过泛型,容器知道自己存放的是什么数据类型,也就不需要转型。

7.异常处理:JAVA的异常处理就好像在程序正常执行路径外并行的处理错误的另一条路径。JAVA中的异常不能被忽略,必须被处理,处理完会返回原中断点继续执行,就像是调试校正,增强了程序的健壮性,而且强制要求开发者在可能出现异常的地方进行捕获处理。

8.数据的存储,对象存储在堆中,对象引用存储在堆栈中,常量和程序代码放在一起或者存在ROM中,其他还有完全独立于程序的永久数据。
基本类型数据比较特殊,不通过new,而是直接存放一个值,存在堆栈中。通过基本类型的包装器类可以在堆上new出一个对象。

9.方法名和参数唯一确定一个方法。

10.表示二进制数:二进制数不能直接表示,只能通过别的数据类型来转换:如Integar.toBinaryString()。得到一个二进制字符串
        表示指数:1.39E-4=1.39 * 10^(-4)   e的大小写表示的是10的指数,如果想用真正的e请使用Math的静态成员E

11.  按位运算符:操作数必须是整型,操作时把操作数看做二进制数。非:~ 一元运算符,把操作数各位取反。与: &
            两个数对齐,每个位进行与运算,生成一个二进制结果。举个例子,可以用一个数&1来判断奇偶。或: |
      移位操作符:左移<<,右移>>, 这两种都是有符号数的移位,另外右移还有一个>>>,把操作数看做无符号数。

12.逗号操作符,只在for循环的控制表达式中使用,可以把逗号连接的几条语句顺序执行,如:
            for(int i=1,j=1;i<5;i++,j--)

13.break直接跳出循环,continue跳转到下一次循环开头
        可以使用标签实现goto类似的效果。标签是一个标签名加冒号,标签后跟一个循环(while或for)
        如一个标签label1:   带标签的跳转:break label1;continue label1;
        break标签:直接跳到标签处,不执行后面的循环
        continue标签:跳到标签处,然后进入标签后面的循环(当前的下一次循环,不是重新进入)

14.switch的判断值必须是int或char

15.构造器保障初始化:必须有一个方法来保证对象在创建时得到初始化,创建和初始化不可分割。为了让编译器找到方法采用名称和类名相同,没有返回值,因为只负责初始化,不给编译器增加负担。

16.方法重载:方法重载有两个原因,一方面为了使用多种创建对象的方式必须重载构造器,另一方面对于语义相同参数不同的方法没必要使用不同的方法名。
重载方法在处理基本类型参数时,参数会寻找与自身相同类型的方法,如果找不到就进行变量提升,让自己变大,如int 变long,(char会提升到int)然后再去找,这是实际参数较小的情况。实际参数大于所有方法参数类型的时候编译器会报错,必须在传入时进行强转。

17.this关键字:this一般用于在方法中作为当前对象的引用,当我们在一个方法中只用方法名调用本对象其他方法的时候实际上编译器会自动帮我们加上这个this。this还有一个特殊用途就是当我们想在一个构造器中调用另一个构造器的时候,直接this加参数,注意这一行必须放在第一行。

18.垃圾回收器工作机制:实际上,垃圾回收器的工作机制并不是唯一的,不同的JVM有不同的处理策略。一般来说,“自适应”方式比较流行。首先明白如何判断一个对象是否存活:认为一个活的对象肯定能在堆栈和静态存储区找到他的引用。这种情况下有两种工作模式,一种是标记清扫,一种是停止复制。标记清扫指的是遍历堆栈和静态存储区的引用,每找到一个活的就进行标记,遍历完成后将未标记的进行清理。这种方式速度很慢,但是非常适合垃圾较少的状态。停止复制工作模式下程序需要暂停,将所有存活的对象复制到新的内存中进行整理。当标记清扫中对空间碎片太多,就切换到停止复制进行整理;同样,当停止复制中发现垃圾较少就切换到标记清扫。
如果我们想要显式清理,可以使用fanalize方法,但是这个方法不代表清理,使用的范围也很小,只是在清理一些垃圾回收器无法识别的资源时使用,也可以用于判断本对象被正确清理。这个方法在清理前调用。

19.初始化:对象的成员变量初始化(定义时赋值)是在构造器前执行的,静态成员变量比较特殊,只在类第一次实例化或者调用类的静态变量时(以及静态方法)调用,调用时所有静态变量都会初始化。初始化顺序是先静态后非静态

20.可变参数列表:在JAVA SE5中加入的新特性,允许方法的参数是可变长度的,如:void add(Integar......numbers)因为要求是对象所以使用了包装类,你可以直接对这个numbers数组进行遍历操作来达到效果。
但是这样会让方法重载出现风险,因为可变参数列表允许不传入任何参数,所以不传入参数调用时两个不同的重载方法有可能无法让编译器做出判断。


21.枚举:枚举的本质是一个类,但是通常只作为一个数据类型。首先需要有几个整型常量定义,然后定义enum:
public enum Fruit{
    APPLE,BANANA,ORANGE}
然后可以这样生成一个泛型实例:Fruit a=Fruit.APPLE。
这个对象可以作为正常的数据类型传入方法,与switch配合最佳:
switch(a){
    case APPLE: break;
}
直接将这个泛型实例与整型常量进行比较即可。

22.控制访问权限的原因:从应用的角度看,类库的消费者不关心类库的具体实现,没必要了解类的具体实现,也不希望客户端的代码有所修改。当类库编写者希望对代码进行重构和优化时,如果没有权限控制,就不知道客户端调用了哪些方法和变量,也就无法修改。借助访问权限控制,类库编写者只向用户提供他们需要的方法,重构也就不会影响客户端代码。

23.包:包可以看做一个类库,包的作用是指定代码中类的唯一性,通过import告知编译器这个类的所属,也可以直接用包名加类名来使用一个类,一般用作者域名反转作为前缀来保证包名的唯一性。我在开发中遇到过使用不同包的两个同名的类,这时候的处理方法是一个类直接用类名(找import的包),另一个用包名加类名使用。代码第一行的pakage也是指出本类的归属,方便在其他地方找到。包还有一个作用,影响成员的可见性。
在引用JAVA官方类库中的类的时候是非常方便的,只需要直接import,但是如果需要使用我们自定义的包中的类,就需要配置电脑的CLASSPATH。他的工作过程是这样的,借助声明的包名和类名,系统在CLASSPATH中遍历文件,找到这个包名和类名对应的.class文件(java文件编译后),然后解释器对class文件进行解释。这里要说到一个.java文件就是一个编译单元,只能包括一个public类型的类。当然我们也可以通过在编译器中引入jar包来使用。
如果我们import static 类.* 的话,就可以直接用方法名使用这个类的静态方法(工具类)
另外,同一目录下的类即使没有显示给出package,也认为是同一个包中的。

24.访问权限:
类成员的访问权限:private仅本类可见,protected包可见及子类可见,public全部可见。可以通过将构造器设置私有来禁止其他对象通过构造器创建类。默认包可见。
类的访问权限:只有默认和public,默认是仅本包内可见。也就是说最低也是包内的类可以看见这个类。


25.代码复用很重要的一个方面是类的复用,方式有组合、继承和代理。组合就是在类中使用已存在的类的实例,也就是用已存在的类生成新类。继承不必多说,子类获取父类全部数据行为。代理在JAVA中并不直接支持,只是一种复用方式,即在新类中定义和旧类同名的方法,然后在里面调用旧类对象的相关方法。这样新类也有了这个方法,通过代码复用。

26.在每一个子类对象内部都有一个父类对象,这个父类对象只能通过构造器生成,而且编译器会在子类的构造器中默认先调用父类的构造器 (无参)。如果父类没有无参构造器,子类必须在构造器第一行使用super(x)调用父类构造器并传入合适的参数。
于是我们可以得出一个更加完整的初始化顺序,首先编译器在解释编译文件的时候注意到这个类有一个父类,于是先到基类中进行static变量的初始化,然后回到子类中进行static和非static变量的初始化,然后进行构造器执行,这其中又会调用父类构造器,父类会进行非静态变量的初始化,之后子类的构造器剩余部分继续执行。

27.final关键字:
修饰成员变量的时候表示一次赋值,不可修改。基本数据类型比较好理解,修饰对象的话就表示这个引用不能再指向其他对象。但是并不能保证这个对象内部的属性不发生改变,这是JAVA无法保证的。修饰方法的参数时候效果类似,方法体中参数值不能更改,引用不能指向其他对象。
修饰方法表示不能被覆盖,针对的是子类。private修饰的方法默认自带final,这也好理解,因为只有自己能看到。
修饰一个类表示这个类不能被继承。

28.继承中的可见性:
能够被子类继承的变量和方法可见性和父类保持一致,public和protected修饰的变量和方法能够被子类拥有,子类可以直接使用变量,或者对方法进行覆盖。父类的private成员子类是没有的。这里有一个问题,如果子类对象调用继承的public方法,方法中使用了父类的私有变量,会不会出问题呢。答案是不会的,我们知道当在子类中覆盖一个方法时默认第一行是用suoer调用父类的方法,每个子类对象中都有一个父类对象,所以其实是父类对象在接收消息调用方法,也就不会有问题。

一.用引用操纵对象 每种编程语言都有自己的数据操纵方式。有时候,程序员必须注意将要处理的数据是什么型。你是直接操纵对象,还是用某种基于特殊语法的间接表示(例如C和C++里的指针)在操纵对象? 所有的这一切在java里都得到了简化。一切被视为对象,因此可采用单一固定的语法。尽管一切都“看作”对象,但操纵的标志符实际上是对象的一个“引用”(reference)。 例如:用遥控器(引用)来操纵电视机(对象)。 分析:1.改变音量,实际操控的是遥控器(引用) 2.四处走动,只要带着遥控器(引用)是电视机(对象),仍旧可以操控电视 3.即使没有电视机,遥控依然可以独立存在 也就是说,你拥有一个引用,并一定需要一个对象与它关联。因此,如果想操纵一个词或句子,则可以创建一个String引用: String s; 但这里创建的只是引用,并是对象。如果此时向s发送一个消息,就会返回一个运行时错误。这是因为此时s实际上没有与任何事物关联(即,没有电视机)。因此,一种安全的做法是:创建一个引用的同时便进行初始化。 String s=”hello”; 但这里用到了java语言的一个特性:字符串可以用带引号的文本初始化。通常,必须对对象采用一种更通用的初始化方法。 一旦创建了一个引用,就希望它能与一个新的对象关联。通常用new关键字来实现这一目的的。new关键字的意思是“给我一个对象”,所以前一例子可以写成: String s=new String(“hello”); 它仅表示“给我一个新的字符串”,而且通过提供一个初始字符串,给出了怎样产生这个String的信息。 当然,String型并非是唯一存在的型,java提供了数量众多的现成型。重要的是,你能盲目的创建型。 二.存储到什么地方 程序运行时,对象是怎么进行放置安排的呢?特别是内存是怎样分配的呢?对这些方面的了解会对你有很大的帮助。有六个同的地方可以存储数据。 1) 寄存器 这是最快的存储区,因为它位于同于其他存储区的地方——处理器内部。但是寄存器的数量及其有限,所以寄存器由编译器根据需求进行分配。你能直接控制,也能在程序中感觉到计算机存在的任何迹象。 2) 堆栈 位于通用RAM(随机访问存储器)中,但通过“堆栈指针”可以从处理器那里获得直接 支持。堆栈指针若向下移动,则分配新的内存;若向上移动,则释放那些内存。这是一种快速有效的分配存储方法,仅次于寄存器。创建程序时,java编译器必须知道存储在堆栈内所有数据的确切大小和声明周期,因为它必须生成相应的代码,以便上下移动堆栈指针。这一约束限制了程序的灵活性,所以虽然某些java数据存储于堆栈中——特别是对象引用,但是java对象并存储其中。 3) 堆 一种通用的内存池(也位于RAM区),用于存放所有的java对象。堆同于堆栈的好处是:编译器需要知道要从堆里分配多少存储区域,也必知道存储的数据在堆里存活多长时间。因此,在堆里分配存储有很大的灵活性。当需要创建一个对象时,只需用new写一行简单的代码,当执行这行代码时,会自动在堆里进行存储分配。当然,为这种灵活性必须要付出相应的代价。用堆进行存储分配比用堆栈进行存储分配需要更多的时间(如果确实可以在java中向在C++中一样在栈中创建对象)。 4) 静态存储 这里的“静态”是指“在固定的位置”(尽管也在RAM里)。静态存储里存放程序运行时一直存在的数据。可用关键字static来标识某个对象的特定元素是静态的,但java对象本身从来会存放在静态存储空间里。 5) 常量存储 常量值通常直接存储在程序代码内部,这样做是安全的,因为它们永远会被改变。有时,在嵌入式系统中,常量本身会和其他部分隔离开,所以在这种情况下,可以选择将其存放在ROM(只读存储器)中。 6) 非RAM存储 如果数据完全存活于程序之外,那么它可以受程序的任何控制,在程序没有运行时也可以存在。器中两个基本的例子是“流对象”和“持久化对象”。在“流对象”中,对象转化成字节流,通常被发送给另一台机器。在“持久化对象”中,对象被存放于磁盘上,因此,即使程序终止,它们仍可以保持自己的状态。这种存储方式的技巧在于:把对象转化成可以存放在其他媒介上的事物,在需要时,可恢复成常规的,基于RAM的对象。Java 提供对“轻量级持久化”的支持,未来的java版本可能会为持久化提供更全面的解决方案。 三.永远需要销毁对象 在大多数程序设计语言中,变量声明周期的概念,占据了程序设计工作中非常重要的部分。变量需要存活多长时间?如果想要销毁对象,那么什么时刻进行呢?变量生命周期的混乱往往会导致大量的程序Bug,接下来将介绍java是怎样替我们完成所有的清理工作,从而大大地简化这个问题的。 作用域 大多数过程型语言都有作用域(Scope)的概念。作用域决定了在其内定义的变量名的可见性和生命周期。在C,C++和java中,作用域由花括号的位置决定。例如: { int x=12; //只有x 可用 { int y=100; //x,y 都可用 } //只有x 可用 //y 超出了作用域 } 在作用域里定义的变量只可用于作用域结束之前。 任何位于“//”之后到行末的文字都是注释。 缩排格式使java代码易于阅读。由于java是一种自由格式(free-form)的语言,所以,空格,制表符,换行都会影响程序的执行结果。 注意,尽管一下代码在C和C++中是合法的,但是在java中却能这样书写: { int x=12; { int x=100; } } 编译器将会报告变量x已经定义过。所以,在C和C++里将一个较大作用域的变量“隐藏”起来的做法,在java里是允许的。因为java设计者认为这样做会导致程序混乱。 对象的作用域 Java对象具备和基本型一样的生命周期。当用new创建一个java对象时,它可以存活于作用域之外。所以假如你采用代码: { String s=new String(”this is a string”) ; } 引用s在作用域终点就消失了。然而,s指向的String对象仍然继续占据内存空间。在这一小段代码中,我们似乎无法再访问这个对象,因为对它唯一的引用已超过了作用域的范围。[后续在说,在程序执行过程中,怎样传递和赋值对象引用]。 事实证明,由new创建的对象,只要你需要,就会一直保留下去。这样。许多C++编程问题在java中就完全消失了,在C++中,最难的问题似乎在于:程序员并能从语言本身获得任何帮助,以确保在需要调用对象时,该对象仍然可用。更重要的是:在C++中,一旦使用完对象后,必须确保要销毁对象。 这样便代码一个有趣的问题。如果java让对象继续存在,那么靠什么才能防止这些对象填满内存空间,进而阻塞你的程序呢?这正是C++里可能发生的问题。这也是java神奇之处所在。Java有个“垃圾回收器”,用来监视用new创建的所有对象,并辨别哪些会再被引用的对象。随后,释放这些对象的内存空间,以便供其他新的对象使用。也就是说,你根本必担心内存回收的问题。你只需要创建对象,一旦再需要,他们就会自行消失。这样做就消除了这编程问题(即“内存泄漏”),这是由于程序员忘记是放内存而产生的问题。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值