Java核心技术卷I

  • 一、Java基础
  • 二、java深入
  • 三、图形程序设计Swing

一、Java基础

1,命名规范:类名首字母大写,若多个单词组成,每个单词首字母大写;

2,注释三方法:句:// 段 /* */ 生成文档注释

3,数据类型:整型—int 4字节,long,8字节, short 2字节, byte 1字节,范围是-128到127。byte与short主要用于特殊场合,如底层文件的处理和需要控制占用存储空间量的大数组

java中,整型的范围与运行java代码的机器无关,但c,c++需要针对不同的处理器选择最为高效的整型,这就很容易造成溢出等问题

浮点型:尽量使用double,在需要单精度数据的库,或者需要存储大量数据的时候再使用float,因为float的精度不够准确

bool类型:只有false和true两个值,不像c,c++可以用0代表false,用非0代表true。

变量:变量名必须以字母开头或数字构成的序列,_,$,也算字母,声明变量后立刻初始化,避免出现问题,java中不区分变量的声明和定义,c,c++区分。

常量:用final修饰变量,变量值只能被赋值一次,无法再进行改变。java中,如果希望某个常量可以在一个类中的多个方法使用,采用类常量: public static final 变量名 = 值。

数学函数:使用方法:Math.方法(参数),常用方法:sqrt 开方,round 方法,可以得到最接近小数的整数,但是需要加强制类型转换,pow 幂运算,其返回值为double类型,如果得到可预测的结果比运行速度更为重要的话,则需要使用StrictMath类。

类型转换:低—>高,自动;高—>低必须强制转换。

二维数组列不统一的解决方法:
举例:int[][] b = new int[2][];
int b[0] = new int[3];
int b[1] = new int[5];
从而解决了二维数组列数不固定的问题。

& 与 &&的区别:
结果不会产生影响,但是&&会有短路效果。
如两侧是boolean类型的,则&是逻辑运算符。否则是位运算符
&位运算符:将数变为二进制处理,同为1则1,否则为0
| 与 || 同理。

字符串:substring方法:可以用来提取子串,使用方法:对象名.substring(起位置,终位置),但是并不会截取终位置的字母,而是停在它前面。

拼接:直接通过 + 号即可。

不可变字符串:因为其源码在string类前加上了final修饰符,使得该类变成了常量类,所以是不可变字符串。不可变的目的是实现字符串共享。
但是在java中可以使用substring方法截取字符串 + 修改补充的字符串,从而实现了字符串内容的改变,但是其实质是创建了一个新的字符串,使变量指向了新创建的字符串,而不是改变了原有字符串的值。

字符串判断相等: == 是判断存储地址是否相同,equals则判断字符串内容是否相同,调用该方法可以是字符串变量,也可以是字符串面量。例如:s.equals(p); “Hello”.equals§,也可以使用compareTo()方法,
String常用方法:核心卷I 50页。

equals底层源码:1,首先判断引用是否相同,如果有则返回true;其次判断对象是否为空,空返回false;2,接着判断两个对象是否属于同一个类,如果不等返回false,3,如果相等,则发生类型转换,为之后的比较作准备,4,转换后,具体一项项内容的比较。java要求equals方法具备以下特性:自反性:非空引用自己判断与自己是否相等,返回true;对称性,如果x是否等于y返回true,则y是否等于x返回也应该为true;传递性,x,y,z;x,y返回true,y,z返回true,则x,z返回true;一致性,如果x,y的引用未发生变化,则x是否等于y返回结果应该不变;对于任意非空引用x,x.equals(null)返回的都是false。

equals的方法的显示参数必须是Object类型,只有这样才能覆盖Object类中的equals方法,不然就是定义了一个新的完全无关的方法。

如果判断Arrays类型是否相等,则是判断长度是否相等 + 对应位置上的元素是否相等 --------> 都相等则返回true,数组元素类型可以是Object,int等类型。

StringBuffer和StringBuilder:皆为可变字符序列
StringBuffer:线程安全,做线程同步检查,效率较低。
StringBuilder:线程不安全,不做线程同步检查,因此效率较高。 建议采用该类。
常用方法:append()添加元素,在末尾添加 insert(x,y)在索引位置x添加元素y
replace(x,y,z)将数组x到y的元素替换为z reverse()反转数组

StringBuilder源码理解:底层是一个变量字符数组,但是与String的不同是其数组不是final的。变量count表示的是底层字符数组的元素的真实个数,而不是数组长度
默认数组长度为16,length返回是字符数组元素真实个数,capacity()返回的是数组容量,capacity意思为容量。
每次添加字符串时要扩容,扩容的默认策略是增加到原有长度的两倍再加2

输入:Scanner常用方法:57页。
常用:nextLine()读取一行,next()读取下一个单词,hasNext()判断是否还有单词,类似的 hasNextInt();

输出:精度表示,和c一样,例如%8.2f,含义是输出占8位,小数部分2位。

文件输入和输出:读取代码举例:Scanner in = new Scanner(Paths.get(“文件路径”),“UTF-8”);对文件进行读取。
输入代码举例:PrintWriter out = new PrintWriter(“文件路径”,“UTF-8”);对内容进行输出,若文件存在则直接写,若文件不存在,则自动创建再写入。详细知识:卷I 61 + 卷II 第二章。

for-each循环:不需要指定元素下标,直接操作,使用方法:
for(值(int element):集合(a)){

System.out.println(element)}

数组:java中允许数组长度为0,允许将一个数组变量拷贝给另一个数组变量,此时,两个变量将引用同一个数组。
可以通过Arrays.copyOf(a,b)方法进行数组拷贝,可以用toString(a)方法进行数组所有元素输出,sort(a)方法进行了数组排序,java中使用的是优化后的快速排序算法,数组常用方法,84页

多维数组:具体研究二维数组,在二维数组中想使用for-each,需要两个嵌套的循环,具体:86页。如果想快速打印二维数组元素,可以使用Arrays.deepToString(a)方法。

Arrays工具类:
sort() 排序
binarySearch(数组,查找值) 二分查找法
toString()返回给定数组的字符串形式
copyOf(复制的数组,需要复制的长度)复制一个数组,并且生成的是新的数组对象
System.arraycopy(复制的数组,复制数组起始位置,需要复制进去的数组,复制数组起始位置,长度)

类与对象:依赖—uses a的关系;聚合—has a的关系;继承:is a的关系
对于对象必须要进行初始化之后才能使用,一种方法是new,另一种方法是引用一个已经存在的对象。

静态变量和静态常量,即static关键字。

静态方法:一种不能向对象实施操作的方法,但是可以操作自身类当中的静态域。以下两种情况使用静态方法:1,不需要访问对象状态,所需参数都是通过显示参数提供;2,一个方法只访问类的静态域;3,工厂方法:此内容设计模式后补充。

java中参数传递问题:实际上java进行的总是值传递,没有引用传递。方法不能改变基本数据类型的参数,可以改变对象参数的状态,不能使对象参数引用一个新的对象,因为虽然传进来的是引用,但是实际上也只是引用的copy。

重载:方法名相同,但是有不同的参数,即实现了重载,返回值不能作为方法是否重载的标准。

构造函数:只有在没有重载的构造函数的时候,系统才会提供一个默认构造函数用来生成对象。
java中可以在一个构造函数中,用this(参数)调用另一个构造函数,这种方法的好处是对于公共的构造函数代码部分只有编写一次即可。
另外也可以通过初始化块对对象进行初始化,这些块只要构造了此类的对象,就会被自动执行,初始化块会在构造函数调用之前就被执行。

类与继承:super关键字:1,用来调用超类的方法;2,调用超类的构造器,在使用super调用超类构造函数的时候,必须将该语句放在第一条。
Java不支持多重继承。

this的使用:
1,类内部使用,防止局部变量和成员变量同名,可以通过this区分
2,调用类的构造方法,并且this调用构造方法的语句必须放在第一行
3,调用成员方法,有时候可以省略

构造函数并不会被继承,一般封闭性较好的类,属性是私有的,可以通过调用父类的公有方法来对父类的参数进行操作,也可以通过super方法调用父类的构造函数。this指向当前对象,super指向父亲的。

多态:向上转型和向下转型,其中向下转型是违法的,不可这样转型。在向上转型的时候,什么引用接收了它,编译器则将它视为该引用,不在视为原本的类型引用。即编译器将子类对象视为了父类对象,并且只能调用父类当中有的方法,不能调用子类当中有父类中没有的方法,否则会报错。在子类数组和父类数组时,不要使用向上转型,否则会产生不管是子类对象还是父类对象都可以存在这个数组的问题,导致数据存储出现问题。

向上转型的时候,调用父类的方法,若子类当中重写了该方法,则虽然发生了向上转型,但是仍然会调用子类方法,发生覆盖现象,但是父类的属性不会被子类覆盖。

多态只和方法有关,和属性无关,如果向上转型调用属性,则调用的是父类的属性。即多态影响不到属性。

方法调用的步骤:首先编译器查看对象的声明类型和方法名,获取所有可能被调用的候选方法,其次,再寻找与参数匹配的方法,这个过程叫重载解析,如果没有找到方法或者很多个方法与之对应,则会报错,接着,如果是private,static,final方法,编译器就会非常精确的知道应该调用哪个方法,称之为静态绑定。如果发生的是动态绑定,虚拟机会调用与对象的实际类型最接近的那个方法,本类中有就调本类,否则查超类,以此类推。并且为了避免时间开销,虚拟机预先为每个类建立了一个方法表,所以在调用方法的时候直接查表即可。

动态绑定的过程:首先,获取对象实际类型的方法表,其次,查找有该方法的类,此时已经知道了该调用哪个方法,最后,调用方法。

阻止继承:如果在定义类前加final关键字,则该类无法被继承,如果在类方法中在final关键字,则该方法无法被覆盖。

强制类型转换:

抽象类:抽象类关键字:abstract,抽象类是将对象共同属性和方法更进一步的抽象,抽象类中可以包含抽象方法,具体数据,具体方法,尽量将通用的域和方法放到抽象类中,无论其是否是抽象的。抽象类起着占位的作用,其具体实现是在子类中,抽象类无法被实例化

内部类:类中再包含类

非静态内部类和静态内部类是包含在类的内部
非静态成员内部类:简单理解就是类中的一个方法,其可以调用外部类的非静态成员变量和方法,内部类的修饰符可以是public,protected,默认,private。内部类可以直接访问外部类中的成员与方法(相当于方法可以访问类的成员),而外部类不能直接访问内部类的成员(相当于外面不能访问方法的局部变量),需要先创建对象,再通过对象进行访问
内部类访问外部类的同名成员:OuterClass.this.成员名称
必须先创建外部类对象,才能创建内部类的对象,非静态成员内部类是属于外部类的。即非静态成员内部类是依附外部类而存在的。
举例:Outer oc = new Outer();
Outer.Inner ic = new Inner();
或者:Inner ic = new Outer().new Inner();

非静态内部类中不能有static关键字
外部类的static关键字修饰的不能访问非静态内部类,包括不能使用非静态内部类定义的变量,创建实例
理由:static关键字修饰的并不需要依靠对象而存在,而是依靠类而存在,加载即有。而非静态内部类是依靠外部类的实例对象而存在。如果在外部类的静态方法中访问非静态内部类,就会产生对象未实例的问题。

静态成员内部类:
其实理解方法就是相当于类的一个静态成员即可。

内部类的修饰符是static:静态内部类只能访问外部类的静态成员,所以内部类调用外部类的同名成员,该变量即是静态变量,直接通过类名.变量名即可。
静态内部类属于整个外部类的,属于类而不是属于具体对象。所以创建静态内部类对象,不需要实现创建外部类的对象
外部类可以通过类名直接访问内部类的静态成员,访问非静态成员必须先创建内部类对象。

这一点的学习可以联系static关键字来进行比较学习。

局部内部类:内部类在方法中。即外部类—方法—内部类。(使用情况很少)
使用情况是:需要一个类完成一个功能和方案,但是这个类只使用一次或者很少,并且不需要这个类是公共可用的,所以就产生了局部内部类。其作用域只在该方法中。
局部内部类访问方法中的局部变量,局部变量必须加上final关键字,JDK8以后final可以省略,但是编译后仍然会加上final,即方法内部类访问的局部变量不可改变。

匿名内部类:是一种特殊的局部内部类。
前提:存在一个类或者一个接口,这个类可以是抽象类。
本质是:只一个继承了该类或者实现了该接口的子类匿名对象
使用范围:适合那种只创建一次的对象的类,如java开发中的线程任务Runnble,外部比较器Comparable。

一个类实现了Coparable接口,但是只有一种比较规则,如果想要多种比较规则,该怎么处理:
comparable:内部比较器,public class Student implement comparable{},内部比较器只能有一个,一般是采用最经常使用的比较规则。
comparator:外部比较器,可以指定多个,不需要Student实现该接口,而是专门定义的类。

匿名内部类只是不起名字,但是模板必须要有,比如new Comparator()并不是创建了接口对象,接口不可以被创建,而是创建了一个实现了Comparator的类对象。

局部内部类和匿名内部类是在类中的方法里面。

受保护访问:protected关键字。如果子类想访问父类的一些域,或者想父类的域对子类也开放,则可以将该域标记为protected。如果想限制某个方法的使用,就可以将它声明为protected,这表示子类被信任,可以使用这个方法,而其他类不行。

访问修饰符:private 本类可见; public 所有类可见;protected 子类和本包可见;默认修饰符:对本包可见。

Object类:所有类的超类,Object类型的变量可以引用任何类型的对象,在java中,只有基本类型不是对象。

Object中比较重要的方法:equals方法,前面已写
hasCode()方法: 后续补充,核心169页。
toString方法:用于返回表示对象值的字符串,在标准库中,很多类都定义了toString()方法,但是最好为自定义类增加一个toString()方法。
数组Arrays下的toString底层代码详解:


首先判断是否为空,然后判读数组长度是否为0,之后再生成一个StringBuilder对象,接收数组方便展示。如果是x.toString()则为返回b当中的数据,如果是Arrays.toString(对象),则是生成了一个新的字符串类型的对象。

对象包装器与自动装箱:
如果想将基础类型转换为对象,则需要用到包装类。

包装类有final关键字,所以无法被继承,而且一旦生成了包装类,包装类当中的属性值无法再被改变,所以也无法用过包装类修改数值参数。

自动装箱:即不需要代码,编译器自动将基础类型转换为包装类,相反的,也会发生自动拆箱。

在包装类当中,不要使用 == 判断,因为可能会将出现的值包装到同一个对象当中,也可能不包含在同一个当中,所以 == 号可能出现问题,推荐使用equals方法判断包装类对象。

包装类在精度转换的时候,由小精度到大精度时候,首先会拆箱转换为大精度的基础类型,然后再转换为大精度的包装类。

包装类源码理解:
Integer:继承了Number类,里面定义了一个final变量value,从而实现将整型转换为Integer类。在Integer类中,提供了一个静态内部类IntegerCache,对于定义一个静态数据cache,长度为256,赋值为-128-127,对于自动装箱,如果数据在-128-127之间,直接获取数据的指定值,对于范围之外的,通过new Integer()重新创建对象,从而提高效率。

接口:关键字:implements,接口是对类的一组需求描述,接口中的所有方法自动属于public,所有在接口中不需要提供关键字public,但是在实现这个接口的时候必须声明为public,否则系统默认为是包可见性。接口中可以定义常量,但是绝对不能含有实例域(个人理解即为对象),现在可以在接口中定义简单方法,但是方法不能引用实例。接口方法的实现以及实例的提供都是由其类完成。
接口比抽象类的好处在于:java只能单继承,而对接口却没有限制,并且接口避免的多重继承的复杂性和低效性。比如说在c++中允许多重继承,就带了来如虚基类,控制规则等复杂特性。

接口默认方法冲突问题:如果一个类继承了超类,也继承了多个接口,接口和超类中都有同名的方法,则优先级为:超类有限,当接口发生冲突的时候,必须在类中实现对该方法的覆盖。

JDK1.8接口新特征:
接口中可以有非抽象方法,static,类不能重写,只能通过接口名调用
如果子类定义了同名静态方法,那就是子类的方法,与接口无关,可以通过子类对象调用

接口中可以添加非抽象方法,default,实现类可以重写,只能通过对象名调用
实现类中可以直接使用default方法,也可以重写default方法,但是default关键字要去掉
上级接口中default方法的调用:MyInterface.super.method();

提供非抽象方法的目的:
减少子类中的重复代码;
方便对Java类库增加新功能,在接口中增加default方法,则相应的接口以及子类都有这个方法,并不需要重新copy。

二、java深入

拟机的构成:
java堆:虚拟机启动时创建,此区域的唯一目的就是存放对象实例,为所有线程所共享
方法区:用于存储已被虚拟机加载的类信息,常量,静态变量,即编译器编译后的代码等数据,为所有线程所共享。

堆内存分为三个部分:年轻代:分为eden区+两个相同大小的存活区
当创建对象时,会被放在eden区保存,经过好几次GC仍然活跃的对象,将被晋级到存活区。存活区一定有两块相等大小的空间,一块用于未来晋升,另一块用于对象回收,在年轻代使用的是MinorGC,GC(GC即垃圾回收机制)采用的是复制算法。每次经过GC的对象,当年龄到一定岁数(默认值为15),就会进入老年代,如果要保存的对象超过了eden区的大小,此对象也会直接被保存在老年代当中,当老年代内存不足时,会引发major GC,即Full GC,即进行回收。永久代用于存放类信息等,即编译后的代码等数据,但是在JDK1.8后,永久代内存空间被取消了,取而代之的是元空间。

为什么废弃永久代:
废除永久代是为了融合HotSpotJVM与JRockit JVM做出的选择,因为后者没有永久代,不需要配置永久代内存空间,并且永久代内存会经常不够用活发生内存泄露。

元空间功能和永久代的区别:永久代使用的是JVM堆内存空间,而元空间使用的是物理内存,直接受到本机物理内存限制。

垃圾回收相关:
主要回收JVM堆内存的对象空间
可以将对象的引用变量设置为null,暗示垃圾回收机制可以回收该对象
垃圾回收对象之前,会先调用它的finalize方法,finalize并不是进行垃圾回收,只是在垃圾回收之前进行最后一步的处理,可以什么都不做,也可以覆盖finalize方法使得对象起死回生。
不要主动调用某个对象的finalize方法
程序员可以通过System.GC()与Runtime.getRuntime(),会有效果,但是具体回收与否由系统控制。
但是GC代码内部其实就是调用Runtime.getRuntime()

异常:

java异常由:try,catch,finally,throw,throws五个关键字进行处理。
try-catch-finally 先进入try执行语句,发生异常则找到异常类型相匹配的catch语句,对异常进行处理,最后执行finally语句内容,finally一定会被执行(除非在try语句中执行了System.exit(0),此时finally语句不会被执行,因为已经结束了虚拟机的运行),catch可能不会被执行。当在try里面发生异常时候,未被执行的语句将不会被执行。

什么情况下catch语句是不执行的:将异常抛出throw e;发生的异常与catch中异常类型不匹配;return。

return与finally语句的执行顺序:执行return之前的语句-----执行finally语句----执行return语句
finally在实际开发中的使用场合:IO流的管理,数据库连接的关闭,socket的关闭。

一段代码可能发生多个异常,可以用多个catch语句分别处理不同的异常。
catch语句的执行是按顺序的,执行第一个与之匹配的异常处理语句
在安排catch语句的顺序中,先捕获最特殊的异常,然后再逐渐一般化,即先子类后父类

异常分为运行时异常:不需要进行处理,系统会自动检测处理。
检查异常:必须捕获进行处理,否则会编译错误。

异常处理:声明异常throws,抛出异常throw

throw抛出检查型异常时,必须在方法签名后使用throws抛出
throws:子类声明的异常范围不能超过父类声明的范围,父类没用声明的异常,子类也不可以。

throw与throws的区别:
1,作用:throws是声明该方法有异常,调用者必须进行处理;throw是将产生异常的地方向上抛出异常
2,位置:throws是紧跟在方法声明的后面,并且后面跟的是类,并且可以是多个;throw是在产生异常的地方,即方法体内部,后面跟的是对象,并且只能跟一个。

自定义异常:直接从Exception类或者其子类的派生出一个子类即可。习惯上,该类要带两个构造器,一个是默认构造器,一个是详细信息的构造器。

Math类常用方法:返回值一般是double类型。
Math.sqrt开方,pow(2,5)2的五次方
abs绝对值,max,min最大最小值
random随机数,默认是[0.0,1.0)

Random类:
虽然math中提供了random方法,但是我们经常需要的并不是[0.0,1.0)的double类型数,并且Math.rando()底层调用的就是Random的nexDouble()方法。
使用方法:
Random random = new Random();
random.nexInt(10),范围是[0,10)的整数
nextDouble(),nextFloat()

枚举:
定义枚举类举例:
public enum Gender{
男,女}
所有枚举类隐性的继承了java.lang.Enum,枚举本质上还是一个类。
当定义一组常量的时候,推荐使用枚举类型。

File类:获取文件或者文件夹的属性,实现对文件,文件夹的创建和删除。
File不能对文件的内容进行操作,需要借助IO流来实现。
代码举例:
File file = new File(“文件路径”); ----- 文件的创建
file.delete(); ------文件的删除。
file.getName—得到文件名字
file.getAbsolutePath()—得到文件的绝对路径
路径中需要追的细节:使用/或者是\,推荐使用/即可。

File[] files = file.listFiles(),得到一个File类型的数组,存储file文件夹下的文件/文件夹。

集合与数组的比较:
相似点:都可以存储多个对象,对外作为一个整体存在
数组的缺点:长度固定,连续存储空间,删除插入效率低下,无法直接保存映射关系,数组缺少封装,操作繁琐。

List:无序,可重复
实现类:ArrayList :内存空间连续,数组长度可变(内存空间连续决定了)------>遍历元素和随机访问效率较高,但是插入删除较为繁琐。
LinkedList:采用双向链表存储方式(决定了)-------->插入,删除效率较高,遍历和删除随机访问效率低下

常用方法:add(x,y)索引为x的地方添加元素y;toString展示;get(x)得到索引为x的值,主意其返回值为一个Object,所有要使用拆箱操作(如果不使用泛型);list2.addAll(x,list1)在集合2中的x位开始将集合1全部加入集合2;clear全部删除;remove(x)删除索引x的值;remove(new Interger(96))删除值为96的;set(x,y)将索引x处的值改为y;contains(y)集合中是否有值为y;list1.removeAll(list2)将list1与list2做差集(list1为主导);list1.retainAll(list2)将list1与list2做交集(list1为主导,并且list是允许元素重复的);isEmpty()是否为空

集合中要使用泛型:
因为集合中存储的是对象,所以可以存储各种各样的,编译的时候并不会报错,造成程序不安全
并且如果不适用泛型,操作繁琐,有些地方需要使用强制转换。

ArrayList底层源码:底层是一个长度可以动态增加的Object数组,StringBuilder底层就是一个长度可以动态增加的char数组

JDK1.7中Arraylist默认长度为10,JDK1.8中默认长度为0,第一次添加数据就要进行扩容
扩容的过程:首先是判断空间是否够,如果不够,则将数组扩容一半,如果扩容一半还是不够,则将数组按照最小所需的容量进行扩容。

LinkedList:

Java中Stack类已经过时,一般使用Deque 双端队列来实现栈,对一端进行限制即可,存在数组队列和链表队列两种实现方式(Queue也是有两种实现方式)

集合框架—Set和Map
Set集合类型:
Set:无序,唯一
HashSet:采用Hashtable哈希表存储结构,优点:增删改查速度快;缺点:无序
LinkedHashSet:采用哈希表存储结构,并且使用链表维持次序,有序(只是保证哈希表中的链表的元素有序,即按添加顺序进行排列)。
TreeSet:采用二叉树(红黑树)存储结构,有序(按自然顺序进行排列),查询比List快,但是没用HashSet快。
Set与List一个区别:
List有三种遍历方式,for循环,for-each循环,iterator迭代器
Set有两种遍历方式,for-each循环,iterator迭代器,因为Set是无序的,不可能提供关于索引
Set相比Collection,没用增加任何方法,而List则增加了与索引相关的方法。

问题1:String对象用HashSet和LinkedSet存储可以保证唯一性,自己定义的类却无法保证?
因为String类已经重写了equals和hascode方法,而自己定义的类并未重写,所以将对象存储在Set里面时,要自己重写equals和hascode方法。

问题2:String对象用TreeSet可以保证有序唯一,但是自己定义的类却直接报错
因为String已经自己实现了comparable接口,而自己定义的类没用实现comparable接口,所以将自定义对象存储在TreeSet时,需要自己实现comparable接口。

Map:
方法:添加:put(key,value)key-----value;map.get(key)按key找值
keyset()得到一个key的集合,values()得到一个Collection,含所有value
遍历方法:1,先得到key集合,再遍历key中获取value-----不推荐
2,直接得到key-value组成的set
举例:Set <Map.Entry<E,E>> entryset = map.entrySet()-----得到一个key-value组成的set
for(Map.Entry<E,E> entry:entryset){…} ----- 遍历输出值

HashMap:key唯一,value可以不唯一,key与value都是无序的,并且key与value都可以为null
key与value其实都是对象。

LinkedMap:key有序,按照添加顺序,key可以为空

TreeMap:key有序,按自然顺序,TreeMap不允许key为null。

HashMap在JDK1.7时,底层是一个table数组+链表


I/O流:
Java中数据的输入输出是以流的方式进行。
流的分类:方向—输入,输出流
处理的数据单元—字节流(以字节为单位,命名以Stream结尾的流一般都是字节流),顶级类是InputStream,OutputStream字符流,以字符为单位获取数据,命名上以Reader/Writer结尾的流一般是字符流,顶级类是Reader,Writer(这四个类都是抽象类,无法创建实例)。
处理对象的不同:节点流,直接从数据源或目的地读写数据,如FileInputStream;处理流,是处理流的流,不直接连接数据源或目的地,一般是对其他流先进行处理以提高程序的性能,如BufferedInputStream,处理流也叫包装流。

1,文件字节流:FileInputStream和FileOutputStream
代码举例:InputStream file = new FileInputStream(“文件路径”);创建输入流
OutputStream os = new FileOutputStream(“文件路径”);创建输出流
以字节为复制单位,即一个字节一个字节的读取:file.read-读,os.write-写
int n = file.read()—每次读一个字节;
while(判断停止条件){
os.write(n);
n = file.read();}
file.close-关闭流。

以字符数组为中转站:
byte[] buf = new byte[1024];
int len = file.read(buf);
while(len != -1){
os.write(buf,0,len);
len = file.read(buf);
}

文件字符流:FileReader和FileWriter,字符流的底层仍然是字节流,字符流进行了封装转换,使得开发者可以更简单的处理非英文字符
字节流可以完成对所有类型文件的复制,而字符流只能完成文本的复制(即txt,java类型的),字符流一般用来处理中文文件
异常处理的流程:创建流,使用流时要使用一次try-catch语句,在关闭流时也要进行异常分析

缓冲字节流:BufferedInputStream和BufferedOutputStream,使用缓冲流可以提高字节流的效率,双缓冲技术就是依靠这个。

代码举例:InputStream is = new FileInputStream(“文件路径”);
OutputStream os = new FileOutputStream(…);
BufferedInputStream bi = new BufferedInputStream(is);默认输入缓冲区为8192
BufferedOutputStream bo = new BufferedOutStream(os);

缓冲流的原理:未使用缓存技术之前,每一个字节的读取都需要从硬盘读取,所有读取速度慢。使用缓冲流后,读取文件的顺序是:先读缓冲区,有数据则不读硬盘。,没用数据则一次读入缓冲区,缓冲区满了自动刷新。

缓存字符流:BufferedReader和BufferedWriter
按行读取文件是:String str = 输入流对象.readLine();
readLine()函数的底层原理:仍然是一个字符一个字符的读取,append()放入到StringBuilder/char[]中,遇到换行符,将StringBuilder/char[]转换成String并返回。

数据流:DateInputStream和DateOutputStream
用文件流,缓冲流只能按字节,字符数组,最方便的也是按行读取,数据流和对象流提供方便操作各种数据类型的方法,直接调用。

数据流只能处理基本数据类型和字符串,对象流还可以操作对象
写入的是二进制对象,无法通过记事本等查看
写入的数据需要通过对应的输入流来读取。

数据流和对象流的代码举例:
1,先生成一个文件输入/输出流对象,2,然后生成一个缓冲输入/输出流对象,将1的对象当中2的参数,3,再生成数据(对象)流输入/输出流对象,将2中生成的对象当做参数,即可完成从文件路径读取数据的操作

序列化与反序列化:
1,序列化:将内存中的对象状态信息转换为可以存储或传输的形式
对象(内存)------------> 字节数组,字节序列(外存,网络)
反序列化:字节序列(外存,网络) ---------> 对象(内存);

2,什么时候需要序列化和反序列化
存储和传输时,比如存储到外存,网络中

3,如何实现序列化和反序列化:相应的类实现Serializable接口
对象输出流对象.writeObject(new 对象)------- 写入
对象输入流对象.readObject(),返回值是一个对象,需要进行强制转换,是从对象输入流的地址文件读取对象。

4,序列化的细节:
序列化接口没用任何方法,但是仍然有用:查看ObjectOutputStream
static属性不参与序列化
如果希望某个属性不参与序列化,需要使用transient修饰

如果对一个序列化类进行了改变,再进行读写等操作就会报错
解决办法:在类中给一个固定的序列化版本号,序列号无法手动编写,必须自动生成。百度serialVersionUID+idea查询生成。

其他流:
1,打印流:PrintStream和PrintWriter,只有输出流,没有输入流
2,转换流:InputStreamReader和OutputStreamWriter,实现字节流到字符流的转换,是适配器模式的应用,只能从字节流转换为字符流,可以带来处理字节的便利,没有字符流转换为字节流,因为没用这种需求
3,Java IO流的设计使用了装饰模式,动态组装流,减少了子类的数量,是继承的一种替代方案。

线程问题:
程序:是一段静态的代码,它是应用程序执行的蓝本

进程:指一种正在运行的程序,有自己的地址空间
进程的特点:动态性,并发性,独立性

并行:多个cpu同时执行多个任务
并发:一个CPU采用时间片方法分配cpu使用时间,宏观上同时执行多个任务。

线程:线程是进程内部的一个执行单元,它是程序中一个单一的顺序流程
线程又被称为轻量级进程
如果一个进程同时运行了多个线程,用以完成不同的工作,则称之为

多线程
特点:轻量级进程,独立调度的基本单位,共享进程资源

1,线程的定义和创建
法1:继承Thread类。run方法是线程要完成的任务,start是启动线程,线程进入等待队列,等待获取cpu资源执行。
代码:Thread th = new 继承类名();

法2:实现Runnable接口,
代码:Runnable runnable = new 实现接口的类名/或者使用匿名内部类。

两种方式的优缺点:
方式1,继承Thread类:Java单继承,无法继承其他类,代码稍微简单
方式2,实现Runnable接口:可以继承其他类,便于多个线程共享同一个资源,代码相对繁琐
实际开发中方式2用的更多。
常用方法:
setName 设置线程名称;getName 返回线程名称
getPriority返回线程优先级;getName 返回线程名称
setPriority设置优先级,优先级的三个参数为:MAX/MIN/NORM(默认) PRIORITY

JKD1.5后提供第三种定义线程的方式,实现Callable接口
前两种方法没有返回值,而实现Callable可以有返回值,并且前两种只能抛出运行时异常,抛出检查型异常需要先将检查型异常转换为运行时异常,而Callable可以抛出所有异常。

实现Callable接口的类,要借助FutureTask类将其对象作为参数传递给Thread类,从而完成启动线程等操作。FutureTask能够完成这项工作的原因是其实现了RunnableFuther接口,而RunnableFuther接口又继承了Runnable和Futher接口,Futher接口里面有get方法,用来得到返回值,如果得不到返回值,则会一直等待,可能会产生堵塞,isDone()方法是用来判断线程是否执行完毕。

实现步骤
定义一个类MyCallable实现Callable接口
在MyCallable类中重写call()方法
创建MyCallable类的对象
在测试类中创建Future的实现类FutureTask对象,把MyCallable对象作为构造方法的参数
创建Thread类的对象,把FutureTask对象作为构造方法的参数
启动线程 再调用get方法,就可以获取线程结束之后的结果。

new创建线程后,进入新生状态,处于新生状态的线程有自己的内存空间,当start方法执行时,进入就绪状态,此时已经具备运行条件,但是不一定立刻分配到CPU,则进入就绪队列,即就绪态,当该线程被CPU调度后,就进入运行态,运行状态是线程执行自己的run方法,当处于运行状态的线程,处于某些情况下进入阻塞状态,如执行sleep方法,等待I/O设备等资源,从而让出CPU,只有当引起阻塞的原因消除后,线程就会进入就绪态,重新到就绪队列中排队等待。
死亡状态是线程生命周期的最后一个阶段,线程死亡的原因有三个:1,正常完成所有工作;2,执行了stop方法,被强制终止;3,线程抛出未捕获的异常。

在什么类中使用Thread.sleep()等方法,就是让该类的线程执行sleep等方法。
线程控制:
join()执行join方法的线程占用cpu资源,之前正在执行的线程进入阻塞状态,让执行join方法的线程先执行。举例:执行b线程;a.join();此时b线程进入阻塞状态,a线程开始执行。
sleep() 线程休眠一段时间,等待时间结束后,进入就绪队列,如果sleep方法执行后,没用其他等待执行的线程,这个时候当前线程不会马上恢复执行。
yield()让正在执行的线程暂停,直接转入就绪状态,如果调用yield方法后,没用其他等待的线程,则当前线程会马上恢复执行
setDaemon()可以将指定的线程设置为后台线程/寄生线程,创建后台线程的线程结束时,后台线程液随之消亡,只能在线程启动之前将其设为后台线程。后台线程的意思是其依托于另外一个线程,当依附的那个线程执行结束,后台线程也会死亡。
interrupt()中断线程,但不是直接中断线程,而是需要被中断线程自己完成处理操作才中断线程,中断并不是结束线程,需要线程本身进行判断,从而达到stop方法的效果。
stop()结束线程,不推荐使用

获取当前线程状态的方法:public State getState() { return jdk.internal.misc.VM.toThreadState(threadStatus); }
线程同步:
问题来源:多个用户操作一个对象引起的问题。例如12360同时卖票问题,多用户取一个银行的钱问题。
解决办法:使用多线程来解决。使用实现Runnable接口的类作为资源管理类,然后创建多个线程对象同时操作分配这个资源管理类对象,实现方法就是创建Runnable对象(发生向上转型),将这个对象作为参数传递给每一个线程。

当多个线程访问同一个数据资源时,很容易出现线程安全问题。所有需要让线程同步,保证数据安全。
线程同步:当x>=2以上线程访问同一个资源时,确保资源在某一个时刻只能被一个线程使用
线程同步的实现方案:1,同步代码块,synchronized (obj){ }
2,同步方法;private synchronized void makeWithdrawal(int amt) {}
3,Lock锁。
1,同步代码块:
synchronized (obj){ } ,其放在run方法里面。
注意要点:必须是引用数据类型,不能是基本数据类型。
在同步代码块中可以改变对象内部的属性值,不能改变对象引用。
尽量不要使用String和包装类Integer做同步监听器,如果使用了,保证代码块中不对其进行任何操作即可
一般使用共享资源做同步监听器即可,如果取钱问题中的钱款资源
也可以创建一个专门的同步监听器,没有任何业务含义
锁的是共享对象资源,对象是obj。
建议使用final修饰同步监听器,避免引用发生改变
同步代码块执行过程:
第一个线程进入同步代码块,发现同步监视器处于open状态,需要close,然后执行其中的代码,在执行第一个线程的过程中,发生了线程切换,线程一失去了cpu,但是并没有发生open操作;第二个线程获取cpu,发现同步监听器处于close状态,无法执行其中的代码,第二个线程也进入阻塞状态;第一个线程再次获取cpu,执行完代码,释放锁进行open操作,此时第二个线程再次获取cpu时,发生同步监听器处于open状态,重复第一个线程的处理过程。

同步代码块中可以发生线程切换,但是后续的线程无法执行同步代码块,因为锁状态为close。
评价:安全,但是效率低下,可能发生死锁。

同步方法:
在方法访问修饰符之后加入synchronized关键字即可
同步监听器都是this,所以对共有资源的操作必须放在资源类中实现。
在run方法中调用同步方法即可
锁加在方法上,对象是this。

Lock锁:
使用lock锁实现线程同步
代码举例: Lock lock = new ReentrantLock(); lock.lock()-----上锁;lock.unlock()-------解锁
如果同步代码有异常,要将unlock方法放在finally语句块

线程通信问题:
首先要保证两者所操作的对象为同一个对象,否则线程间的通信毫无意义
其次,生产者消费者都要加锁,并且锁住的是同一个对象

线程通信的三个方法:
wait () 线程进入等待状态
notify() 唤醒此对象监视器上等待的上一个线程
notifyAll() 唤醒此对象监视器上等待的所有线程

阻塞的三种状态:
普通阻塞:sleep,join,输入输出引起的阻塞
同步阻塞(锁池队列):没用获取同步监视器的线程的队列
等待阻塞(阻塞队列):被调用wait方法后释放锁,进入此队列

sleep和wait的区别:
sleep让线程进入阻塞状态,但不会释放对象锁,wait让线程进入阻塞状态,但是会释放对象锁,进入等待此对象的等待锁定池;
两者进入的阻塞状态也是不同的队列
wait只能在同步控制方法和同步控制块中使用,sleep可以在任何地方使用

Lock锁实现线程通信:
前两者实现线程通信,在调用wait方法后,生产者和消费者进入同一个等待队列,所有存在本来打算调生产者,结果调用了消费者的问题,而Lock锁让生产者和消费者线程在不同的等待队列中,从而解决了这个问题。

Condition:
用来代替传统Object额wait,notify方法,并且使用await,signal方法实现线程之间协作更为高效和安全。
其能够更加准确的控制多线程的休眠和唤醒,对于同一个锁,可以创建多个Condition,从而在不同的情况下使用不同的Condition,一个Condition包含一个等待队列,一个Lock可以产生多个Condition,所以可以有多个等待队列
Object监视器模型,一个对象拥有一个同步队列和等待队列,而Lock拥有一个同步队列和多个等待队列
Condition中的方法需要在lock与unlock之间使用,即需要在Lock锁的保护之内

volatile关键字:强制线程每次在使用的时候,都会看一下共享区域最新的值
问题来源:共享数据使用未被更新


synchronized解决该问题:
1 ,线程获得锁
2 ,清空变量副本
3 ,拷贝共享变量最新的值到变量副本中
4 ,执行代码
5 ,将修改后变量副本中的值赋值给共享数据
6 ,释放锁

原子性是指在一次操作或者多次操作中,要么所有的操作全部都得到了执行并且不会受到任何因素的
干扰而中断,要么所有的操作都不执行,多个操作是一个不可以分割的整体。

count++不是一个原子性操作,可以被其他线程打断

保证原子性的操作:
java.util.concurrent.atomic包(简称Atomic包),这个包中的原子操作类提供了一种用法简单,性能高效,线程安全地更新一个变量的方式

AtomicInteger原理 : 自旋锁 + CAS 算法



悲观锁和乐观锁
synchronized和CAS的区别 :
相同点:在多线程情况下,都可以保证共享数据的安全性。
不同点:synchronized总是从最坏的角度出发,认为每次获取数据的时候,别人都有可能修改。所以在每 次操作
共享数据之前,都会上锁。(悲观锁)
cas是从乐观的角度出发,假设每次获取数据别人都不会修改,所以不会上锁。只不过在修改共享数据的时候,会
检查一下,别人有没有修改过这个数据。
如果别人修改过,那么我再次获取现在最新的值。
如果别人没有修改过,那么我现在直接修改共享数据的值.(乐观锁)

并发工具类:
并发工具类-Hashtable:
Hashtable出现的原因 : 在集合类中HashMap是比较常用的集合对象,但是HashMap是线程不安全的(多线程环境下可能会存在问题)。为了保证数据的安全性我们可以使用Hashtable,但是Hashtable的效率低下。

并发工具类-ConcurrentHashMap基本使用:
ConcurrentHashMap出现的原因 : 在集合类中HashMap是比较常用的集合对象,但是HashMap是线程不安全的(多线程环境下可能会存在问题)。为了保证数据的安全性我们可以使用Hashtable,但是Hashtable的效率低下。
基于以上两个原因我们可以使用JDK1.5以后所提供的ConcurrentHashMap

总结 :
1 ,HashMap是线程不安全的。多线程环境下会有数据安全问题
2 ,Hashtable是线程安全的,但是会将整张表锁起来,效率低下
3,ConcurrentHashMap也是线程安全的,效率较高。


总结 :
1,如果使用空参构造创建ConcurrentHashMap对象,则什么事情都不做。 在第一次添加元素的时候创建哈希表
2,计算当前元素应存入的索引。
3,如果该索引位置为null,则利用cas算法,将本结点添加到数组中。
4,如果该索引位置不为null,则利用volatile关键字获得当前位置最新的结点地址,挂在他下面,变成链表。
5,当链表的长度大于等于8时,自动转换成红黑树6,以链表或者红黑树头结点为锁对象,配合悲观锁保证多线程
操作集合时数据的安全性

并发工具类-CountDownLatch:让某一条线程等待其他线程执行完毕之后再执行

总结 :

  1. CountDownLatch(int count):参数写等待线程的数量。并定义了一个计数器。
  2. await():让线程等待,当计数器为0时,会唤醒等待的线程
  3. countDown(): 线程执行完毕时调用,会将计数器-1

并发工具类-Semaphore:
使用场景 :可以控制访问特定资源的线程数量。
实现步骤 :
1,需要有对象管理资源
2,当有线程进来了,发通行许可证
3,当有线程出去了,收回通行许可证
4,如果通行许可证发完了,那么其他线程只能等着

线程池:
起源:创建和销毁对象十分耗费时间,对于经常创建和销毁,使用量非常大的资源,对性能影响很大
思路:创建多个线程,放入线程池中,使用时直接获取引用,不使用则放入池中。

线程池的创建:

基础加强:
1,类加载器
2,反射
3,xml 直接看pdf即可。

三、图形程序设计Swing

补充内容:
Vector,可以用来存储任何类型的数据;

游戏图形界面开发基础:
swing组件:
按钮(JButton):


框架:Frame:

布局管理器:
面板的默认布局管理器:FlowLayout
窗口及框架的默认布局管理器:BorderLayout





产生闪烁的原因:p77页。

定时器:

设置游戏难度:

键盘控制移动举例:
P82页
游戏中的碰撞检测
图像绘制
游戏声音设定

Java基础读书笔记相关推荐

  1. Java基础复习笔记系列 九 网络编程

    Java基础复习笔记系列之 网络编程 学习资料参考: 1.http://www.icoolxue.com/ 2. 1.网络编程的基础概念. TCP/IP协议:Socket编程:IP地址. 中国和美国之 ...

  2. java基础巩固笔记(6)-注解

    2019独角兽企业重金招聘Python工程师标准>>> java基础巩固笔记(6)-注解 标签: java [TOC] 注解(Annotation),也叫元数据.一种代码级别的说明. ...

  3. 【Java基础学习笔记】- Day11 - 第四章 引用类型用法总结

    Java基础学习笔记 - Day11 - 第四章 引用类型用法总结 Java基础学习笔记 - Day11 - 第四章 引用类型用法总结 4.1 class作为成员变量 4.2 interface作为成 ...

  4. java基础巩固笔记(5)-多线程之线程并发库

    2019独角兽企业重金招聘Python工程师标准>>> java基础巩固笔记(5)-多线程之线程并发库 标签: java [TOC] 本文主要概述java.util.concurre ...

  5. Java基础复习笔记系列 七 IO操作

    Java基础复习笔记系列之 IO操作 我们说的出入,都是站在程序的角度来说的.FileInputStream是读入数据.?????? 1.流是什么东西? 这章的理解的关键是:形象思维.一个管道插入了一 ...

  6. Java中大数据数组,Java基础学习笔记之数组详解

    摘要:这篇Java开发技术栏目下的"Java基础学习笔记之数组详解",介绍的技术点是"java基础学习笔记.基础学习笔记.Java基础.数组详解.学习笔记.Java&qu ...

  7. Java基础知识笔记-11_2-Swing用户界面组件

    Java基础知识笔记-11_2-Swing用户界面组件 这章教程两个版本,一个语法是非lambda表达式版本,另一个是lambda表达式版本 非lambda表达式版本 1 Java Swing概述 J ...

  8. 尚学堂JAVA基础学习笔记_2/2

    尚学堂JAVA基础学习笔记_2/2 文章目录 尚学堂JAVA基础学习笔记_2/2 写在前面 第10章 IO技术 1. IO入门 2. IO的API 3. 装饰流 4. IO实战 5. CommonsI ...

  9. java虚拟机读书笔记 第三章 垃圾收集器和内存分配策略

    java虚拟机读书笔记 第三章 垃圾收集器和内存分配策略 GC需要完成的三件事情:哪些内存需要回收.什么时候回收.如何回收 垃圾回收器在对堆进行回收前,首先要确定那些对象存活,哪些对象已经死去,判断的 ...

最新文章

  1. 面向对象--内部属性类型
  2. python半年能达到什么程度_花半年的时间能把Python自学到什么程度?
  3. 中查出所有姓张的学生为啥查不出来_只有笔试成绩没有面试成绩是什么原因 教师资格面试成绩怎么查...
  4. jenkins即将重启问题
  5. java设计模式-Observe
  6. hdu 4006 The kth great number 很复杂的线段树,至少对于我来说,但也学会了很多!!!!!!!!!!
  7. CentOS 5.5 使用 EPEL 和 RPMForge 软件库
  8. Xshell5(远程终端工具)工具的安装使用 【免费】
  9. C# TextBox输入校验方法
  10. 微软语音合成助手 v1.5.1 文本转语音工具,真实语音AI生成器
  11. pytyon 微妙_字体的微妙力量
  12. Mac小白——如何查看网速
  13. python爬虫爬取豆瓣图书
  14. PC市场如何再起波澜?荣耀的创新或是答案
  15. 使用Java实现矩形 平行四边形 等腰三角形 菱形 倒三角
  16. 几款常用LED显示屏驱动芯片介绍及选择VK1624
  17. 做城市规划设计,如何下载地形图?
  18. argparse.ArgumentParser() 用法解析
  19. 句柄(Handler)到底是什么?
  20. 龙乡设置县前后社会纵横

热门文章

  1. ajax初级示例(下拉列表联动)
  2. jQuery+Ajax实现表格数据不同列标题排序
  3. 20180316 三对角矩阵
  4. 天梯赛2022年比赛试题,进阶题L2,登顶题L3试题(仅题目)基础题L1有代码,请看本专栏另一篇
  5. JavaScript 数组拼接打印_8种常见数据结构及其Javascript实现
  6. 真实图形学(光照模型)
  7. 【数据可视化】免费开源BI工具 DataEase 之 Tab 组件前世今生
  8. 【洛谷】P1747 好奇怪的游戏(bfs)
  9. 测试创新——自动化数据清理方案
  10. 欧洲共同语言框架的b2-c1,德语语法A1-A2-B1-B2-C1