Java编程思想


这是一份试图提纲挈领的读书笔记,《java编程思想》这本八百多页的书娓娓道来地包含了太多细节,这对读者是非常贴心的,我也强烈建议细细读这本书,如果你希望在短时间内学会java这种语言,那么这本书不是最好的选择,你可以看看谭浩强系列。我把看这本书的过程中,个人觉得每一章中最重要的思想、用整理在这里,希望自己回顾的时候有所参照和提高。也希望大家带着同样的目的来看本篇读书笔记。

第一章 对象导论
    比起过程型语言编写的程序,用面向对象语言编写的程序更加简单、易于理解、可复用。《c++编程思想》里也有这一章,是一个抛砖引自己的玉的章节,不明白可以跳过,回头再看。

第二章  一切都是对象
    java语言里面,一切都是对象,并且程序员是通过引用来操纵对象。一个简单的例子,非常轻松地让读者进入java的世界。需要注意的是java数据会储存在5个不同的地方:寄存器、堆栈、堆、常量存储、非ram存储,用new创建的一般对象都放在堆中,而特殊的基本对象放在堆栈中,如果想把基本对象也放在堆中,需要包装基本类型。

第三章 操作符
    java中的操作符语法类似于c,所以学习起来一点困难也没有。要特别注意两个比较大的整数相加或者相乘的时候的溢出问题,用long或者biginteger解决这个问题。

第四章 控制执行流程
    我想起《pointer on c》这本书第一章就有这一句话,本书适合那些希望迅速学习一门新语言而不是被“为什么if和for很重要”的弱智问题耽搁进度的读者。呵呵,这一章很不厌其烦地介绍了运算、操作符优先级、类型转换、选择循环等基本特性,有c或者c++编程经验的读者可以大概浏览一下。

第五章 初始化和清理
    关于初始化:
        1.初始化很重要,一定不要忘记。而且java编译器会很好的防止使用未初始化数据的意外,这是比c和c++更优的地方。
        2.编译器初始化的顺序为
            a.类首次加载的时候,有关静态初始化的所有动作都会执行。
               a1.类的加载包括首次创建该类型的对象,或者该类的静态方法/静态域首次被访问
               a2.静态域的初始化在一切初始化之前,即静态变量散布在代码不同的地方,它们也会在任何方法(包括构造器)调用之前被初始化
            b.当用new calssname()创建对象的时候,会在堆上开辟足够的存储空间,这块存储空间被清零,然后执行字段的初始化动作。(这里的字段初始化都是非静态的,因为静态的变量已经在a中执行完毕,而且静态变量存储在不同的地方,静态数据只占用一份存储空间)
            c.执行构造器

    关于清理
        c++关于清理的部分包含很大不确定性。目前需要知道的事情是,正常情况下,我们是不需要调用finalize方法的,而且垃圾回收区会自动回收不再使用的对象,同时我们需要自己注意一些需要关闭的文件。

需要注意的是,用=对数组进行“赋值”的时候,实际上是引用的传递,就是说,二者指向同一堆。

第六章 访问权限控制
    关于包
        你应该有一个自己的域名,这样发布你的java程序的时候,就可以将你的包名设置为你的域名倒转。想要正确让包工作,要正确设置classpath,对于新手来说,这的确是一个挑战。我当初就难到了。
    关于访问权限修饰词
        值得注意的是,如果两个编译单元放在同一个目录下并且都没有设置包名的话,他们对于对方都是拥有包访问权限的。访问权限修饰词是修饰方法和数据,而不是类。类只有两种访问权限,包访问权限或public访问权限。默认为包访问权限。如果不希望其它任何人对该类拥有访问权限,可以把所有的构造器设置为private。但是有一个例外,可以通过该类自己的static成员内部创建(于是就有了工厂设计模式和单例设计模式)。

第七章 复用类
    有三种方法复用类:组合,继承,代理。
        组合即是在新的类里面放上已经定义的类的对象,然后通过调用它的方法来实现自己的功能。
        继承是通过extends关键词继承某一父类,这样就能访问父类的所有public方法(因此为了继承,一般的规则是将父类的所有数据成员都指定为private,将所有的方法都指定为public)。子类的初始化需要注意的是,(当创建了一个子类的对象时,该对象包含一个基类的子对象)java会在子类的构造器中插入对基类默认构造器的调用。但是如果没有默认的基类构造器,或者想调用一个带参数的基类构造器,就必须用关键词super显式地编写调用基类构造器的语句,并且配上适当的参数列表。
        代理很有意思,(我们姑且使用导出类和基类这样的字眼,但要清楚我们不是在讨论继承里面的关键词)在导出类里保存一个基类的对象,然后用自己的方法对该基类的种种方法进行包装。
        如何决定使用哪种方法复用类呢?is-a就继承,has-a就用组合。而且,组合比继承总体上使用更广泛、代价更小。

    向上转型
        这个就牛逼了,第八章,第九章,第十章都与此密切相关。看完本书之后印象最深的就是向上转型了。

    使用final的原因有很多种,一定要弄清楚为什么使用final,是由于设计还是效率。
        final作用于数据的时候:final作用在基本对象比如int上,该值就成为不可改变的,一旦被初始化就无法再被更改,但是作用在普通的对象引用的时候,final使引用恒定不变,但是引用指向的对象是可变的。编译器需要我们确保final对象一定要被初始化,我们可以通过在构造器中初始化他们,以达到相对自由的效果(称为空白final,我认为这个名字容易让人误解)。java允许在参数列表中以声明的方式将参数指明为final,这一特性主要用来向匿名内部类传递数据(这很重要)。
        final作用于方法的时候,说明作者想保持该方法在继承的过程中不被改变,并且不被覆盖。同时,被final修饰的方法会被关闭“动态绑定”,这样编译器就会为final方法调用生成“有限”有效的代码。之所以说有限,是因为随着编译器的牛逼,它生成的代码越来越有效。
        final作用于类的时候,即是作者声明对该类的设计不允许任何继承。

    学习得更深入一些,可能对以下事实感到有兴趣:java中所有的事物都是对象,每个类的编译代码都存在于电脑中的文件夹里(文件夹的层次根据反转域名得到),该文件只有在需要使用程序代码时才被加载。具体的说,就是“类在其任何static成员函数(包括构造函数)被访问时加载”。


第八章 多态

多态的重要基本原理就是向上转型:继承允许将对象视为它自己本身的类型或其基类型加以处处理。
将一个方法调用和一个方法主题关联起来称为绑定,java中所有的方法都是后期绑定(除了static方法和final方法),所以我们可以编写只与基类打交道的程序代码,并且这些代码对所有的导出类都可以正确运行。
(为什么static不动态绑定:因为static方法的主要用法就是用类名.方法名这样的方式来调用,不存在“发送消息给某个对象,让对象判断自己怎么做”这样的情况。
为什么final不动态绑定:这是早期final的一种用法,由程序员指定某方法为final,意味着程序员明了动态绑定的机制,并且声明该方法不需要动态绑定,这样可以获得更好的性能。这种用法已经很少使用了。)
初始化的时候,导出类的构造函数会自动调用基类的默认构造函数,此过程一直递归到最基本的基类。如果需要调用有参数的构造函数就需要手动执行。反过来,如果需要进行清理工作(大部分时候我们都不需要),务必手动执行基类的清理工作先。比如继承链的每个类都实现dispose()方法,那么执行某个类的清理工作的时候,需要手动调用super.dispose()。不过此种情况下,务必在执行super.dispose()之前释放成员对象,清理顺序与执行顺序是相反的。
此外,构造器方面有更加复杂的调用机制,我们不用理它,只需要知道一条有效的准则“用尽可能简单的方法使对象进入正常状态,如果可以的话避免调用其它方法”。
java编译器能够允许向上多态,就是因为java的机制能保存对象的类型信息,即rtti,正因为这种机制,java编译器也允许向下转型,以获得扩展类的“扩展出”的方法。(另,扩展类“扩展”了方法的这种继承不是“纯继承”,这样做好不好?用户自己度量)。向下转型失败的话会抛出一个classcastexception。

虽然这一章都是在讲多态,但是多态并不总是解决问题最好的方案,它有可能使事情不必要地复杂起来,我们应该总是优先考虑更加灵活的组合

第九章 接口
一种专门提供“接口”的类叫抽象类,若含有至少一个abstract方法,该类就必须被声明为abstract的。抽象方法没有方法体,派生类必须实现它,否则派生类也必须被生命为抽象的。
interface关键词使抽象的概念更进了一步:1.这个“类”完全抽象。2.一个类可以向上转型为多种interface。要让一个类遵循某个特定接口,需要使用implement关键字。
在这一章中出现了“策略设计模式”这个词。创建一个能够根据所传递的参数对象的不同而具有不同行为的方法,被称为策略设计模式。
策略设计模式跟适配器设计模式联合使用可以提供非常强大的功能,比如我们遇到了无法更改的类(别人编写的),想要它满足我们的接口然后放到设计模式里面去(当然满足了接口之后的用法就不止如此了),就可以编写一个适配器,包装该类同时产生我所需要的接口。


使用抽象类和接口的两个原因是:1.在多重继承关系中(这真的很常见,看看java api就知道了),导出类可以被向上转型为每一个接口。2.防止客户端程序员创建该类的对象。那么我们该使用抽象类还是接口呢?事实上,如果知道某事物应该成为一个基类,那么第一选择应该是使它成为一个接口
接口之间的继承能够形成很好的体系,更像我们的现实生活。但是要特别注意的是,在不同接口中使用相同的方法名通常会造成代码可读性的混乱,令人不快。
工厂方法设计模式是又一个重要的设计模式。我们在代码中增加额外的间接性,一个重要的原因是想要创建框架




[1]无论你的程序有多大(也可以说不管有多少个类),一定都会有一个main()来作为程序的起点。
[2] System.out.printSystem.out.printIn的区别,很多人其实一直没注意,快捷键用习惯了吧,?。都是在控制台打印结果,区别就是:前者打印后不换行,后者(带In的)打印后自动会换行。
[3]写代码如果你老是只会写一个个不相关类组合在一起,来完成功能,也许你会缺失设计的灵性,有种设计手法叫“继承“大家都知道,子类会继承父类的方法,也就是说,子类会自动获取父类的功能(提高了类的重用性)注:当父类被final修饰的时候,它无法当爹,它是无法被继承的(为什么要用这个来修饰类,为了保持类的严谨性,避免被篡改,使用的场景:String类就是最好的例子)。当父类的方法被final修饰的时候,它是可以被子类继承使用,但是请注意你是无法重写(Override) 该方法。
[4]类跟对象的关系:类不是对象,却是用来创建对象的模型,也可以说类是对象的蓝图。对象是已知的事物,对象会执行的动作。对象本身已知的事物被称为:实例变量(instance variable)俗称属性。对象可以执行的动作称为:方法(methods)。
[5]真正的面向对象的Java应用程序只会让对象与对象交互,所以请多多以对象的角度考虑问题,编码。
[6]变量根据作用域分为:全局变量,局部变量。根据类型分类:primitive主数据类型和引用。主数据类型主要有:boolean char byte short int long float double。注:编译器不允许将大杯的内容放到小杯中,但是反过来可以。
[7]关于引用我觉得有必要拿出来特别讲讲:如Dog d = new Dog();事实上没有对象变量这样的东西存在,只有引用(reference)到对象的变量,对象引用变量保存的是存取对象的方法,它并不是对象的容器,而是类似指向对象的指针(你可以这么理解,它就是这个对象的遥控器,而不是这个对象本身)。
[8]无论被声明来承载的是primitive主数据类型或对象引用,数组永远都是对象。
[9]对象有状态和行为两种属性,状态影响行为,行为影响状态。大家可以好好理解这句话。
[10]在声明方法的时候,方法的参数叫做形参,如:void bark(int numOfBarks),numOfBarks就是形参。当我们调用方法的时候我们会传入实参,:Dog d = new Dog();d.bark(3);,这里的3就是实参。注:对于传入的实参如果是主数据类型的时候,无论方法内部执行什么,都不会改变这个实参,传入的时候的值。但是传入的实参如果是引用的时候,就会改变这个对象,特别注意引用是这个对象的遥控器,当然可以改掉这个对象的东西。
[11]方法有声明返回的类型,void这代表没有返回任何东西,其他的声明类型的话,说好了要返回,就是要返回相应的数据结果。
[12]传入与传出方法的值类型可以隐含地放大或是明确地缩小。这句话大家也好好思考一下哈。
[13]封装的基本原则:将你的实例变量标记为私有的,并提供公用的gettersetter来控制存取动作。封装的意义:可以保护你的对象状态不会随意的被乱修改,如果要修改你的对象的状态,他必须要调用你的setter方法,在这个setter方法中,你可以添加你想要的数据要求约束条件,来保证你的状态的修改值是合法。
[14]实例变量与局部变量之间的差别:实例变量可以不用初始化就有默认值,局部变量没有默认值!如果在变量被初始化前就要使用的话,编译器会显示错误!
[15] ==equals()的区别:哈哈这也是老问题了,使用==来比较primitive主数据类型,或者判断两个引用是否引用同一个对象。使用equals()来判断两个对象是否在意义上相等。(什么?不好懂?通常的用法就是如果比较字符串就用equals,如果比较数字是不是相等就用==。?)
[16]我们在设计编写类的时候,最好遵循下面的顺序:找出类应该做的事情,列出实例变量和方法,编写方法的伪码,编写方法的测试用程序,实现类,测试方法,除错或重新设计,完成。也就是三个大步骤:伪码,测试码,真实码。
[17]关于for循环分为两种:第一种基本(非加强版)的for循环,如:for(int i=0;i<100;i++){};第二种(加强版)的for循环,如:for(String name :nameArray){};注:请多写写不一样的好代码,切莫一个挫代码能用就行的心态,到处写,这会让你的技术停留不前。
[18]关于Java API的小常识:以javax开头的包就会知道他以前曾经是扩展。API是程序员的得力帮手,好工具,所以要善于查询API并熟练的掌握,会让你编码更从容,优雅。大家可以上java.sun.com的网址里面去在线查找API,也可以下载离线到本地。
[19]使用import会把程序变大吗?编译过程会把包或类包进去吗?答案:importCinclude并不相同。运用import只是帮你省下每个类前面的包名称而已。程序不会因为用了import而变大或变慢。
[20]为何我不必importString类或System类?答案:要记得java.lang是个预先被引用的包。因为java.lang是个经常会用到的基础包,所以你可以不必制定名称。java.lang.Stringjava.lang.System是独一无二的classJava会知道要去哪里找。
[21]继承父类,并覆盖(Override)方法后,可以让子类更好的扩展从父类继承下来的功能,如:public void roam(){super .roam();//your code},注意super的作用,它可以让你在父类的方法上,加上额外的行为,这是非常重要的一种写法!一定要学会使用。
[22]继承的意义:避免了重复的程序代码,定义出共同的协议。是多态的一种手法,如:Animal myDog = new Dog();方法的参数跟返回结果都可以使用这种多态的手法,可以大大的提高你的方法的扩展性。
[23]覆盖的原则:参数必须要一样,且返回类型必须要兼容,不能降低方法的存取权限。
[24]还有一种方法叫重载(overload),重载的规则:返回类型可以不同,不能只改变返回类型,可以更改存取权限。注:重载跟多态毫无关系,重载的使用情景在本类中,重写的使用情景在继承的时候体现。
[25]接口是一种100%纯抽象的类,它是多态java的重点。
[26]抽象类的声明方法:在类的声明前面加上抽象类关键词abstract就好,如:abstract class Animal{public void roam()};大家想一想为什么要设计出抽象类,原因就在于:像Animal这样的对象太抽象,不应该被实例化出来,编译器不会让你初始化出现类,如果你要想使用它,那就继承它吧?。
[27]抽象的类代表此类必须要extend过,抽象的方法代表此方法一定要被覆盖过。如果你声明出一个抽象的方法,就必须将类也标记为抽象的。你不能在非抽象类中拥有抽象方法。注:子类也可以不用实现抽象方法,?,只要你把子类也声明为抽象类,让后让你子类去继承你,让它去实现这个抽象方法,这个机制叫抽象机制,将实现的负担转给下层。这个手法很重要。
[28]就算你不知道,但实际上所有的类都是从对象给继承出来的,没有直接继承过其他类的类会是隐含的继承对象。
[29]使用Object类型的多态引用是会付出代价的,因为Object实在是辨识度太低,很多时候都要必须强制转换类型才能用,很不方便。而且最重要的一点就是:就算你知道对象有这个功能,编译器还是会把它当作一般的Object来看待。编译器只管引用的类型,而不是对象的类型。注:(不好懂?哈哈,如:Object o = al.get(index);o.bark();//这个o其实是一个Dog类型的对象,有bark()的方法,但是因为是编译器只知道他现在是Object类型,这个类型是没有该方法的,所以编译器不会通过编译,懂了吧?)。
[30]当你在不知道是不是可以把Object o对象转化成Dog类型,怎么办?简单:if(o instanceof Dog){Dog d = (Dog) o;}
[31] Java为什么不支持多重继承,因为会导致“致命方块”,什么是“致命方块”,举个例子:当你继承两个父类的时候,恰巧两个父类有一样方法,但是里面的实现却不一样,你说做儿子的怎么知道我会继承那个方法,?,这就是问题,编译器也不知道,所以这就是不支持多重继承的原因。

[32]接口可以用来解决多重继承的问题,却又不会产生致命方块这种问题,因为它根本就没有实现,所以木有关系。


[1]程序员会在乎内存中的两种区域:对象的生存空间堆(heap)和方法调用及变量的生存空间(stack)。实例变量存在于所属的对象中,所以自然是在堆上面。局部变量和方法的参数都是被声明在方法中,它们是暂时的,且生命周期只限于方法在放在栈上的这段期间(也就是方法调用至执行完毕为止)。
[2]当你调用一个方法时,该方法会放在调用栈的栈顶。实际上被堆上栈的是堆栈块,它带有方法的状态,包括执行到哪一行程序以及所有的局部变量的值。
[3]无论对象是在哪里声明的,它总是运行在堆上。
[4]到底为什么要知道栈跟堆的机制?答案:如果想要了解变量的有效范围(scope),对象的建立,内存管理,线程(thread)和异常处理,则认识栈跟堆是很重要的。
[5]关于构造器的知识:Duck myDuck = new Duck();看起来像是在调用Duck();方法,其实并不是,我们是在调用的Duck的构造函数。注:唯一能够调用构造函数的方法就是新建一个类。public Duck(){};构造函数的规则:名字一定要跟类的名称相同,而且没有放回类型。
[6]特别注意,当你没有写构造函数的时候,编译器会帮你写一个无参数的构造函数。不过如果你已经自己写了一个构造函数,还想要一个没有参数的构造函数的话,不好意思,你必须自己写一个,因为这个时候,编译器是不会帮你写无参数的构造函数了。
[7]有时候有默认值的无参数构造函数是不合理的,因为可读性太差,也没有太大实际意义。在Java API中有些类就没有无参数的构造函数,如Color类。
[8]在创建新对象时,所有继承下来的构造函数都会执行,这样的过程被称为“构造函数链(Constructor Chaining)“,调用父类构造函数的唯一方法就是调用super();但是你会说我好像自己没写啊,相信你已经猜到了,对!就是伟大的编译器帮忙加的,而且只会执行父类没有参数的构造函数。
[9]一个类有多个构造函数的话,从某个构造函数调用重载版的另一个构造函数也是可行的,好处是可以减少很多重复的代码。做法就是:调用this()this(aString)就行。this就是个对对象本身的引用。this()只能用在构造函数中,且她必须是第一行语句!每个构造函数可以选择调用super()this(),但不能同时调用!
[10]实例变量的寿命与对象相同。如果对象还活着,则实例变量也会是活的。
[11]如果一个方法运行在堆栈的时候,她里面的变量时活着的,它的状态会被保存,但是只有这个方法在栈顶时候才能使用,这才是他真正的使用范围。也就是说局部变量只能在声明它的方法在执行中才能被使用。
[12]一个对象什么时候会从堆里面消失,就是这个对象失去了所有的引用,就被会被垃圾回收给干掉。
[13]当你把引用设为null时,你就等于是抹除遥控器的功能。换句话说你找不到你的遥控的对象了。对null引用使用圆点运算符会在执行期遇到NullPointerException这样的错误。
[14]什么是静态的方法,有一种说法就是:一种不依靠实例变量也就不需要对象的行为,它只要类名去调用即可。
[15]让一个类不被实例化的方法有两种:一种是将类声明称abstract抽象类,第二种是将其构造函数标记为私有的。
[16]静态的方法不能调用非静态的变量,无法引用到该类的任何实例变量。
[17]静态变量:它的值对所有的实例来说都相同,这就是静态变量的功用:被同类的所有实例共享的变量。实例变量:每个实例一个。静态变量:每个类一个。切记!
[18]静态的final变量时常数,常数变量的名称应该要都是大写字母!一个被标记为final的变量代表他一旦被初始化就不会改动。有个比较好玩东西叫静态初始化程序(static initializer)是一段在加载类时会执行的程序代码,它会在其他程序可以使用该类之前就执行,所以很适合放静态final变量的起始程序。如:class Foo{final static int x;static{x=42;}}
[19] final的变量代表你不能改变它的值。finalmethod代表你不能覆盖掉该methodfinal的类代表你不能继承该类。
[20]java5之后开始加入的autoboxing功能能够自动地将primitive主数据类型转换成包装过的对象!
[21]关于日期类,Calendar cal = new Calendar();无法通过编译,因为是抽象类。你可以Calendar cal = Calendar.getInstance();其实是取了他的一个子类?。
[22]静的最高点!静态的import,如果你直接import static java.lang.System.out;class WithStatic{ public static void main(String[] arg){ out.printIn("");}}。就可以这么写,不过完全不推荐这个写法,程序会变的更易混淆,代码的可读性太差。
[23]如果trycatch块有return指令,finally还是会执行!流程会跳到finally然后再回到return指令。看见没有,finally就是这么牛逼!
[24]自己在写方法,内部自己throw异常的话,throw下面的代码都不执行了,切记!
[25]异常也是多态的,如果你throws的是一个父类的异常,这代表此方法同样可以抛出他的子类。
[26] catch的技巧,异常要从小写到大,这样才会让你的异常获取的处理的更准确合理,因为catch从上往下执行,如果catch到了,就不执行下去了,切记!。
[27]如果你自己方法有异常,却不想处理,你也可以继续throws出去(也叫做duck),这样调用你方法的人就要处理了,哈哈。
[28]异常处理规则:1.catchfinally不能没有try。2.trycatch之间不能有程序。3.try一定要有catchfinally。4.只带有finallytry必须要声明异常。
[29] GUI方面的开发,主要用的包javax.swing,不过实在是真没什么人用,看看就好了。里面对一些按钮或者事件的监听注册,对于android开发程序员来说,非常的基础。
[30]内部类可以把外部的方法或变量当作自己的,这就是为什么内部的类非常好用的原因,除了跟正常的类没有差别之外,还多了特殊的存取权。使用场合就是在一个类里面对不同的按钮写不同的内部监听类来解决不同的响应问题。



[1]将被序列化的对象写到文件中,然后就可以让你的程序去文件中读取序列化的对象并把它们展开回到活生生的状态。序列化的文件时很难让一般人阅读的,但他比纯文本文件更容易让程序恢复会原来的状态,也比较安全,因为一般人不会知道如何动手动脚改数据。
[2] Stream这类串流需要两两连接才能做出有意义的事情,一个是低层的串流,一个高层的串流,这样的设计是出于面向对象考虑,一个类只做好一件事。同时通过组合不同的串流,又可以变化出很多花样。
[3]序列化的对象保存了实例变量的值,因此之后可以在堆上带回一模一样的实例。比较叼的就是当对象被序列化时,被该对象引用的实例变量也会被序列化。且所有被引用的对象也会被序列化。。。。最棒的是,这些操作都是自动进行的!
[4]如果要让类能够被序列化,就实现SerializableSerializable接口又被称为markertag类的标记用接口,因为此接口并没有任何方法需要实现的。如果某类事可序列化的,则它的子类也自动地可以序列化(接口的本意就是如此)。
[5]如果一个类中有引用到其他不可序列化的对象,那么这个类也是无法序列化的,切记!如果有变量不想被序列化的,则用transient来修饰变量即可。那些变量主数据类型值会变成默认值,引用的变量会变成null
[6]如果有两个对象都有引用实例变量指向相同的对象对怎么样?例如两个Cat都有相同的Owner对象?那Owner会被存储两次吗?答案:序列化聪明得足以分辨两个对象是否相同,在此情况下只有一个对象会被存储,其他引用会复原成指向改对象。
[7] FileInputStreamObjectInputStream组合使用的时候,如果os.close();的话FileInputStream会自动跟着关掉!
[8]被序列化的对象在解序列化的时候,它的构造函数是不会执行的!切记!
[9]静态变量,它们会被序列化吗?答案:不会。要记得static代表“每个类一个”而不是“每个对象一个”。当对象被还原时,静态变量会维持类中原本的样子,而不是存储时的样子,自己测试哈。
[10]关于File有个很有用的功能就是它提供一种比使用字符串文件名来表示文件更安全的方式。
[11]缓冲区的奥妙之处就在于使用缓冲区比没有使用缓冲区的效率更高。因为每趟磁盘操作都会比内存操作要花费跟多时间。通过BufferedWriterFileWriter的链接,BufferedWriter可以暂存一堆数据,然后到满的时候再实际写入磁盘,这样就可以减少对磁盘操作的次数,从而提高效率。
[12]如果想要强制缓冲区立即写入,只要调用writer.flush()这个方法即可
[13] Stringsplit()可以把字符串拆开成数组。
[14]每台服务器上都有65536个端口,但是我们自己在写测试socket的时候,用的端口都要在1023之后,因为0~1023都已经被保留给已知的特定服务。
[15] Socket serverSocket = new Socket("127.0.0.1",5000)这是建立服务器的Socket连接写法。Socket sock = serverSocket.accept();当用户连上来的时,此方法会返回一个Socket(在不同的端口上)以便与客户端通信。SocketServerSocket的端口不相同,因此ServerSocket可以空出来等待其他的用户。
[16]当启动多线程的时候,我们会觉得同时在做好几件事情,其实只有真正的多处理器系统能够同时执行好几件事,所以其实多线程只是在执行空间里面非常地快速来回交换执行而已。
[17] Runnable threadJob = new MyRunnable();,该类就是你对线程要执行的任务的定义,也就是说此方法会在线程的执行空间运行。
[18]一旦线程执行.start();线程会进入可执行状态,它会在可执行与执行中两种状态中来来去去,同时也有另外一种状态:暂时不可执行(又称为被堵塞状态)。
[19]当多个线程同时操作同一个对象的时候,容易发生数据错误的情况,解决方式就是同步化方法,synchronized。当对象有一个或多个同步化的方法时,线程只有在取得对象锁的钥匙时才能进入同步化的方法。从而保护数据的安全。(不过代价就是损耗性能,需要自己权衡)同时注意死锁的情况。
[20]每个Java对象都有一个锁,每个锁只有一把钥匙。类也有个锁,?
[21]高级java类设计中,泛型的使用是非常高明的,什么是泛型,只要你看到<>就是了,其实到处都是。以泛型的观点来说,extend代表extendimplement。哈哈,不看不知道哈
[22]数组的类型是在运行期间检查的,但集合的类型检查只会发生在编译期间,在多态赋值的时候,集合可没那么容易让你过哈,?
[23]远程过程调用的设计,要创建4种东西:服务器,客户端,服务器辅助设施和客户端辅助设施。其实真相就是两个辅助设施两者在负责通信,客户端跟服务器只是把命令给他们,结果拿回来而已。


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值