- Ref to CC
- 小知识点
- 编译
- 常用Unicode字符
- 字符串
- 数组
- 对象和方法
- main
- 输入输出
- 注意可能出现的异常注意throws标记可能出现异常的方法
- 异常
- 大数
- foreach语句
- 包
- 继承
- Object类所有类的超类
- 泛型数组列表ArrayList
- 对象包装器Warpper与自动装箱拆箱
- 令方法支持可变参数参数数量可变
- 枚举类
- 反射reflection
- 异常
- 接口
- lambda表达式
- 内部类
- 代理
- 异常
- 断言
- 日志
- 泛型
- 集合
- Set集元素有序的Collection
- 库中的具体集合
- JAR文件
==~/.bashrc ~/.bash_profile==
==Windows:Add ‘jdk/bin’ to $PATH==
==类库文件的源代码需要解压后才能看得到:jdk/src.zip==
语言只是实现目标的工具,而不是目标本身。—James Gosling
public class HelloWorld{
public static void main(String[] args){
System.out.print("Hello Java world!");
}
}
java -version
//现在最新的JDK版本是1.8
Ref to C/C++
- C++的
>>
没有指定执行的是算数还是逻辑右移,在执行的时候回采用其中一种效率较高的右移;==在Java中使用>>
将使用符号位填充最高位(即逻辑右移);使用>>>
使用0来填充最高位(即算术右移)==; - Java的=={}代码块中不允许定义与块外重名的对象,即不允许内层覆盖外层==。
- 与C/C++相同:switch-case不能使用float或double
- Java的命令行参数是不包含程序名的,arg[0]就是第一个参数
- ==Java数组的最右侧的[]可以不包含大小,可以为空==
- 特别注意==Java不能使用
class_type instance_name
的形式来声明对象,如:C++可以使用QLabel label的形式来声明实例,但是Java不行,==Date date==并没有声明实例,要么用=new Date(),要么用另一个同类型实例拷贝给它(引用拷贝) - 上一条直接声明而没有new并不是不行,只不过必须清除这个变量并没有关联任何一个对象实例,==此时它就等于NULL==
- Java的new其实就去让对象变量与一个实例关联(返回对象的实例引用)
- Java的所有对象都是在堆中构造的,所以构造对象必须使用new,而C++允许在栈中构造:Foo bar(args); //这种格式在Java是不可行的
- C++ 中允许类的方法在类外定义,当==C++的方法在类外定义的时候,合法;在类内定义的时候,自动成为inline方法==;==*Java的方法必须在类内定义,但是并不意味着inline,是否inline由Java虚拟机根据方法大小、调用频率自动决定;*==
- C#和Java都没有多重继承,而是使用了接口类
- 一般认为远程Java代码是不安全的,一般浏览器都禁用Java插件
- Java的
foreach
参考了C#,所以并不能老是觉得C#山寨了Java - 一般建议少用C++的多重继承
小知识点
- 适用于嵌入式设备:Java ME(Micro Edition)
- C#和Java都没有多重继承,仅C++有
- Java有两种可以执行的代码,字节码和二进制机器码代码,其中,字节码可以编译为二进制机器码,其中,Java跨平台特性就是字节码在发挥作用,同一套字节码在不同平台使用jvm解释
- .NET也是基于虚拟机实现的
- 与C/C++不同,Java的int确定是32位的
- 在网页中运行的Java程序即是applet(applet并没有流行起来,竞争对手:JS、Flash、HTML)
- Java一开始叫Oak(橡树)
- Java可以免费获取、查看源代码,但是不能修改和再发布,应该属于闭源的(不完全属于开源)
- 目前,Java的许可和专利都可以免费获取,但是嵌入式JavaME是需要支付专利费的,不过这个专利差不多到期了,马上就可以免费了
- 早期的Java是解释型的语言,现在是即时编译,速度应该与C++相差无几,相对于C++只是开销更大,GUI更慢
- 早期Java出现过很多安全漏洞,导致很多公司禁止浏览器中安装Java
- ==JavaScript与Java的’关系’:JavaScript原名叫LiveScript,开发JS的公司叫Netscape,曾经帮助过Java开发;JS的语法和名称与Java很像,除此之外,没有任何其他联系了;JS是一种网页脚本语言;JS的应用与applet相似,但是JS的功能更强,局限更小==
- JRE:Java Runtime Enviroment:==JRE实际就是Java虚拟机==
- Java没有任何unsigned类型
- ‘>>’逻辑右移(高位补符号位);’>>>’算术右移(高位补0)
- 注意:==&&的优先级高于||,它们同时出现的时候,并不是使用从左到右的顺序,而是&&优先==
- Java程序的路径:IDE控制或命令行的CWD或虚拟机的启动路径;注意==相对路径需要以以上几个路径作为基准==
- ==Java支持带标签的break;类似于goto,将标签放在要跳出的语句块的前面,因为break会跳出到标签指示的语句块的末尾==
- 特别注意:==类的三种常见的关系:依赖(use-a),聚合(has-a),继承(is-a)==
- use-a:一个类内实例化另一个类作为数据成员,这个数据成员的生命周期与该类相同,故为依赖,use-a
- has-a:聚合,一个类与另一个类实例有联系,但不是类内成员,没有生命周期的直接关系,因此是聚合
- is-a:只有当是is-a的时候,才可以==考虑==继承
- OOP语言之父:Smalltalk(修正:Smalltalk是面向对象编程之母),是Objective-C和Java的重要参考对象
- Java之前的‘为虚拟机生成代码’的思路:Lisp、Smalltalk、Pascal都采用了这种思路
- JDK是开发Java使用的,JRE是运行Java软件所需要的、用户使用的环境,开发Java并不需要JRE
- 使用/* * 和*/生成文档注释
- 数字可以加下划线,前缀0b表示二进制
- 没有后缀的浮点数默认为double后缀d
- 少用char,不用float
- 对负数求余会得到-1
- print的时候可以使用+将double与String连接起来而不需要将double转为String
- ==String不是内置类型,而是类库的类型==
- ==任何一个对象都可以转换为String==
- ==在基本java.lang包中定义的对象可以不用import,但是其他包就必须使用import==
- Java不允许在{}子语句块内覆盖定义同名变量,否则编译错误。可以在/* 文档注释中使用HTML
- ==特别注意:在C/C++/Java中浮点数比较大小、比较相等不等的时候注意浮点数不一定能精确表示,因此需要慎重处理判断条件和使用的判断符号==
- 注意:一般不要使用switch(),或者少用
- 注意:==break和continue都不提倡使用==
- ==特别注意:Java二维数组相当于C++二级指针==,而不是相当于二维数组或一维数组指针。
- 静态成员不是不属于类,==静态成员属于类但是不属于类的实例,但不意味着实例不能访问静态成员,静态成员是类实例的共用属性==
- Java与C++一样:允许在类方法中使用this来区分类属性和方法参数/变量
- ==*注意:new一个数组只是初始化了数组对象,并没有初始化数组成员,需要对数组成员调用构造器构造对象才能使数组内确实冲入了元素(使数组成员引用绑定到对象而不是null)*==
- ==*一个对象变量可以表示多种不同实际类型,即为多态。多态:(在继承树中)多个不同的类使用自己的代码来完成同一个方法,即同一个方法在继承树中的各个类有自己的执行代码。多态的时候,子类如果使用了父类的方法,则此时不能使用自己类内的属性,只能使用所在类的属性,因为此时动态绑定到了父类。*==
- ==*子类可以当做父类使用*==
- ==java.lang.reflect.Array.copyOf()可以拷贝数组,并且可以使用第二个参数来指定新数组的大小,因此可以实现数组大小的扩展。==
- 特别注意:==*类型转换中,可以将一个类型转换为Object,由于Java会记住对象的信息,因此可以将Object转回这个类型。但是,如果一个Object对象一开始就是Object(即不是从类型Foo转换而来的),那么它是不能转换为这个类型的,忽略这个事实进行转换将会导致抛出ClassCastException。*==
- ptotected的保护作用其实并不大,因为protecred对于该类的子类来说是可以访问的,用户想要访问protected的时候,只要继承一下就行了。而使用final禁止继承的话,protected不如直接改成private,因为它本来就final没有子类了,protected等同于private。==另外,同一个包内的其他类可以访问protected,protected的保护作用对它们无效。
- ==特别注意:除非父类的所有方法继承下来都有实际作用,否则,不要使用继承,即使满足’is-a’也不要使用。==
- ==注意:Java程序运行后是虚拟机内的一个线程,而只有JVM自己是进程,Java程序不是独立的进程,只是JVM下面的线程。==
- Toolkit.getDefaultToolkit().beep()可以响铃。
- Java定时器是javax.swing.Timer,java.util.Timer不是定时用的,而是用于后台任务调度。
- 可以使用接口类型的变量来引用实现了这个接口的对象实例。
- 一个对象数组是否支持排序,就看它的元素对象有没有实现Comparator接口。
- 拷贝一个对象使用clone()方法,实际执行的是浅复制。==特别注意:clone()方法默认执行的是浅拷贝:它只拷贝当前对象的所有域,但是对于这个对象内的子对象,clone()没有拷贝子对象,拷贝得到的对象与原有的对象使用同一子对象,因此,一般需要重新定义clone()以执行深拷贝,除非子对象不会改变(确定在程序中不会被修改的实例、不可变的实例(String等))才不用重新实现clone()。==
- clone()使用浅拷贝,==浅拷贝不会拷贝对象中引用的子对象==。如果子对象是不可变的,那么浅拷贝和深拷贝实际没有任何差别,因为不可变的子对象并不会改变,因此浅拷贝的子对象共用并不会影响两个类实例;而可变的子对象就需要进行深拷贝,否则因为其中一个实例修改了这个子对象就会导致另一个类的子对象也改变(因为他们实际是引用了同一个子对象)。
- 在类的名称前面加一个’a’或’an’得到的新名字可以表示一个该类的实例,可以使用这个方法来命名实例或用来举例子。
- 如果对象能够使用浅拷贝,则实现Cloneable接口以支持clone,如果必须要深拷贝,则重新定义clone()。==clone()方法protected继承自Object,Cloneable接口并没有定义clone(),但是如果不实现这个接口而发生了’请求克隆该对象实例’的操作,将会抛出异常。
- ==标记接口==:这种接口里面并未定义任何方法,它用于指示并确保类实现了某些方法,如上面的Cloneable接口,并未定义clone(),但是如果类没有实现clone()将出现异常,允许使用instanceof来检查。==CoreJava建议:一般不要使用标记接口==。
- ==即使Object.clone()能够满足要求,也应该实现Cloneable接口并且实现方法clone():调用super.clone()。==
- 深拷贝的实现方法:在clone()里面使用使用super.clone()之后再对子对象分别手动调用clone()。
- 注意:==Object.clone()返回Object,必须对clone的返回值做类型转换:Foo foo_cloned = (Foo)super.clone();==
- ==涉及对象克隆的时候,注意,如果存在可能不能克隆的情况,必须在方法中声明可能出现异常:throws CloneNotSupportedException,并且使用try-catch捕捉起来==。
- 标准库仅仅5%的类实现了克隆。==所有数组类型都实现了克隆。==
- 特别注意:==Integer等基本类型对应的类是不可变的==。
- GUI中的’双缓冲’:在GUI中某些部件的操作是对其对应的内存或数据结构进行操作,如绘制屏幕的时候相当于对一块内存的读写;此时,如果直接操作这个内存,会导致窗口部件在频繁的绘制中发生闪烁,不仅降低视觉体验,且造成更多的绘制事件带来的开销。采用双缓冲可以解决:对一块同样大小的内存进行操作,操作完成后再将该内存覆盖到目标内存,这样只需要一次绘制即可实现屏幕更新,开销更小,且不会发生闪烁。
编译
- 编译器:javac 将.java编译为.class
- ==注意:javac 将java编译为class,但是实际使用java命令来执行java程序的时候注意不要带上后缀.class;== 即
javac foo.java
得到foo.class,运行使用java foo
而不是java foo.class; ==运行时,只需要提供类名而不要带上任何扩展名== - Java代码–>编译–>体系结构无关/中立的字节码(可以解释运行)–>(即时)翻译–>对应CPU的机器码–>全速运行
- 将执行最频繁的字节码翻译为机器码:即时翻译
- ==Java特性:解释型:体现在Java解释器解释字节码==
- 注意,==早期的Java是解释型的,并不能翻译为机器码,没有翻译功能。==
常用Unicode字符
- \u2122 即™
字符串
==String类型不是Java内置的,而是标准Java类库的一个预定义类==
1. 必须使用equal()方法来判断字符串相等,==不能使用’\==’来判断两个String是否相等,这样判断的只是两个String对象是否存在同一位置==;因为Java对字符串的实现类似char *,它的==判断只能判断是否存于同一内存地址
2. 区别空串和NULL串
3. ==任何一个Java对象都可以转换为字符串String,因此可以直接使用+连接String和其他对象==
4. ==Java字符串是不可变字符串:不能直接修改Java String;只能通过构造一个新的String并让变量引用这个对象才能实现修改String==
例:
foo = bar.substring(0,11)+"test"; //构造了新的String并修改foo的引用实现修改foo
方法
- substring()
- 与C++ iter、Python [:]切片相同:都是左闭右开区间
- join()
- arg1作为后面其他arg的分隔:
- 例:
java
String.join('/','S','M','L','XL');
//"S/M/L/XL"
- equals()
- compareTo()
- 作用同
equals()
,但是一般不使用compareTo()
- 作用同
使用StringBuilder和StringBuffer来构造String
- StringBuilder和StringBuffer都是构造字符串的类,可以使用.append()来增加内容,最后使用toString()来得到String对象,适用于多次、频繁地构建、修改String的时候;
- 区别:多线程构造String的情况下应该使用StringBuffer
数组
==注意:C++风格的数组定义:int a[]是可用于Java的,只不过int[] a比较清晰==
定义格式
int[] arr = {1,2,3};
new int[] {1,2,3}; //无名数组
int[] arr = {10};
arr = new int[] {10};
注意:==是String[] foo = new String[10];
不是String[10] foo;==
==注意:Strig数组new之后,数组元素不是空串而是NULL串,要么通过for()对每个元素赋值(修改元素引用),否则它们是null而非空串==
数组拷贝
==注意,数组名称使用=来复制的时候,实际是引用了同一个数组对象,即两个数组名实际指向同一个内存==
例:
int[] list_a = list_b;
list_a[1] = 10; //list_b也被改变
注意事项
int[] arr = new int[10];
/*此时仅仅初始化了数组对象,但是它的成员都没有明确绑定到一个确定的成员对象,即此时它们都是null*/
for(int i : arr){
arr[i] = i; //令元素绑定一个对象,此时真正完成数组成员初始化
/*当数组成员是对象时,这里对应使用new*/
}
数组列表
如果需要动态扩展数组大小,应该使用ArrayList
==’=’实际进行的是引用拷贝==
使用Arrays.copyOf、copyTo()实现值拷贝
==与C++的相同:Java的数组几乎等同于C++中存放在堆heap中的数组:
int[] arr = new int[10];
等同
int * arr = new int[10];
使用Array.toString(arr)可以将数组的所有元素以[,,,]的格式输出
使用匿名数组初始化数组变量的时候不需要new,但是修改数组变量的引用目标(即对它赋值的时候需要new)
int[] arr = {1,2,3};
arr = new int[] {4,5,6};
允许数组长度为0
可以通过交换多维数组中的某一行的引用来实现行的交换
double[] tmp = arr[0];
arr[0] = arr[1];
arr[1] = tmp; //利用行的引用交换实现多维数组行的交换
数组方法
import java.util.Arrays
- Array.sort()
- … P84
不规则数组
不规则数组即每一行的大小不同
int [][]odds = new int[MAX+1][];
for(int n=0;n <= MAX;++n){
odds[n] = new int[n+1]; //第n行有n+1个元素
}
Java二维数组相对于C++
double[][] arr = new double[10][10];
相当于C++:
double** arr = new double*[10];
对象和方法
对象的三个主要特性:
- 对象的行为
- 对象的状态
- 对象的标识(身份、识别)
- 隐式参数implict:即方法调用的时候的this;
- 定义常量域:使用final,相当于const
- Java的static关键字的作用与C++完全相同,被static定义的类的域,表明这个域是静态的,不属于类也不属于类的任何一个实例的,即有多少个类都只有这一个共用的静态域(方法或属性)
- 静态方法static,不需要实例化类就能够直接调用,如Math.PI,而不需要实例化
- ==注意,静态方法不属于类,也不能对类对象进行操作==
- this就是隐式参数
- 注意静态成员的访问形式:
Math::PI
Math.PI
使用this来在一个构造器中调用另一个构造器
格式:this(args);
例:this("hello",10);
这样可以调用另一个构造器,对于==C++,不能调用其他构造器,只能把构造器中重复的代码封装到一个方法再在构造器中调用==
构造顺序
- 域初始化语句和初始化块
- this调用的构造器
- 执行构造器
==所有的类都来自于超类Object==
实例域
在类中,数据成员因实例的不同而不同,因此称数据成员为实例域
方法的参数的传递方式
====*注意:Java中的方法的参数传递仅仅只有拷贝传递!不论是传递基本数据类型(int等)还是传递对象类型实例,都是进行拷贝传递!*====
对于类类型的对象,Java拷贝对象实例的引用后传递,而不是拷贝一份对象实例后传递;即:在C++中,方法传递对象实例的时候,传递的是对象的引用;Java中,首先拷贝一份该对象的引用,然后传递这个引用。
==总之,Java仅仅只有值拷贝传递,并没有引用传递。==
==*注意这种拷贝引用后传递的形式,与引用传递的不同:引用传递和拷贝引用后进行拷贝传递都能够通过传递过来的对象参数来调用参数的方法,但是拷贝后值传递的本质是一个拷贝后的引用,存在不同;例:*==
swap(Test x, Test y);
Test t1,t2;
swap(t1,t2); //这个方法仅仅只能交换t1和t2拷贝后的引用,实际t1、t2没有被交换
func(Test t)
{
Test new_t = new Test();
t = new_t; //对方法参数t的引用拷贝赋值,使该引用指向新的对象,对外部的对象没有影响,仅仅只是减少了一次引用计数(方法调用的时候增加了一次引用计数)
}
==*总结:一个方法不能修改基本数据类型的参数(因为它们是值传递);一个对象可以调用对象参数的方法来修改这个对象的状态,但是不能让对象参数指向一个新的对象(比如赋新的值、交换对象(即赋值))*==
方法的“签名”即C++中的函数原型(==但是不能包括返回值类型==)
==*注意:方法的返回值不是重载的依据,仅仅只有返回值不同的函不能构成重载,重载要求参数不同*==
对象初始化块和静态初始化块
{
//对象初始化块
}
static
{
//静态初始化块
}
==注意对象和静态初始化块的区别:静态初始化块仅仅只执行一次,一个类的n次实例化只在第一次执行这个静态初始化块,而对象初始化块则每次实例化都会执行==
可以为类添加实例关闭时的最后一个方法finalize
但是最好不使用它,毕竟书上没有给出格式和方法,资源最好在用完就释放,如文件描述符或句柄。
main()
main()方法是静态的,因为它并不依赖对象进行操作
在模块/类中使用main()来实现模块测试
====*在一个模块/类中增加main()方法可以实现对这个类/模块的单元测试*====
对于工程内的一个模块,可以对其使用一个main()来编写一个单元测试而不影响整个系统的功能,即测试代码和功能代码可以共存。
原理是:一个工程的主模块中的main()作为系统的启动,而此时单元模块没有main(因为它并不是主模块),只有本类的构造器。
当需要对这个模块:类进行单元测试的时候,就对这个类新增一个main()方法,测试代码写在main()内,并使用javac和java命令来编译执行这一个模块,这时该模块可以独立运行单元测试,因为它拥有了自己的main()方法。同时,它并不影响已有系统的编译和运行,因为工程只是使用了类的构造器,并不关心类内的main()
不要在类中使用过多的基本类型
将这些基本类型的数据成员封装到一个类中,这样比较清晰
将职责过多的类进行分解
输入输出
import java.util.*;
使用Scanner的一个实例关联到System.in标准输入流后才能读取输入,并不能直接从System.in中直接读取
方法
- nextLine();
- next();
- nextInt();
- nextDouble();
密码输入
Scanner类不能禁止回显,因此输入的密码永远可见。
必须使用Console类:它可以使用readPasswd()来读取密码并禁止回显。
==*特别注意:使用readPasswd()得到的密码,为了安全,必须使用cha[]一维数组来接收,不要使用String!*== 因为String可以被共享且String不能修改,因此可能会被读取到。另外,密码数组不使用后,必须用各种值填充之,以抹去密码。
输出
System.out.printf()沿用了C语言的printf(),并且新增了一些%标志
- %+:打印正、负数的符号
- %空格:在正数之前增加空格
- %0:补0
- %(:将负数包含在括号内
- %,:使用逗号分隔数字
- %#:对float打印出小数点或对十六进制数、八进制增加0x、0
- %1 d、 x:对第一个参数十进制打印、第二个参数十六进制打印。==注意参数索引中%n后面一定不要忘了$==
- %
文件I/O
同样使用Scanner来进行读写
Scanner in = new Scanner(Path.get("test.txt","UTF-8"));
PrintWriter out = new PrintWriter("Test.txt","UTF-8");
错误:new Scanner(“test.txt”);
导致Scanner将参数解释为String而不是文件名,此时,该Scanner操作的数据就是参数本身各个字符组成的字符串。==Path.get()不可或缺==
注意可能出现的异常,注意throws标记可能出现异常的方法
==使用shell重定向命令将文件绑定到System.out、in就可以不打开这些文件以完全不用考虑异常问题==
异常
对于可能发生异常的方法,使用throws ExceptionName
标记方法以通知编译器此方法可能抛出异常
public static void main(String[] args) throws IOEXception
{
PrintWriter out = new PrintWriter("","UTF-8");
}
大数
import java.math.*;
BigInterger、BigDecimal;
foreach语句
格式
for(int iter : names) //注意:不是foreach(:)
System.out.println(names[iter]);
注意:foreach(:)只能按照一维数组处理,并不能直接处理多维数组
==处理多维数组必须使用嵌套的foreach()语句==
for(double[] rows : arr)
for(double value : rows)
System.out.println(value);
使用Arrays.deepToString(arr)来快速打印多维数组
包
import java.util.*
静态导入
import static java.lang.System.*
之后可以直接使用out和exit()而不再需要写出类名System
打包
package com.foo.project
在安卓中出现的com.开头的东西就是java包
在包中编译和运行
编译需要使用javac加上对应源文件.java的路径,而运行它则以包名称来运行:java com.foo.project
包内部内容的权限
public:任意使用
private:只允许定义它们的类使用
什么都不写:只允许包内使用,相当于包内的protected
继承
类似C++,使用extends替换冒号
public class Foo extends Bar
{
}
==注意:Java仅仅只有公共继承,并没有私有继承和保护继承==
private域仅仅只允许本类去访问,即使是子类也不能访问父类的private
子类不能访问父类的private域
==特别注意:子类并不能访问父类的private域,而只能通过父类给出的接口来访问。注意,private只允许本类可以访问,即使是派生的子类也不允许访问父类的私有域==
使用super来调用父类的方法
super.foo();
C++:
BaseClassName::foo();
注意使用超类名而不是实例名,因为它仅仅只是指示编译器调用父类方法,并不是通知编译器使用另一个实例的方法
使用super(args)来调用父类构造器
super(args);
Foo::Foo(string str,double num) : Bar(int index)
{
//...构造函数主体
}
==利用父类构造器完成对父类私有成员的初始化,毕竟子类没有办法访问父类私有成员==
默认构造器(与C++稍有不同)
在==Java中,没有任何参数的构造器即为默认构造器,在C++中,不仅没有参数的构造器是默认构造器,所有参数都有默认值的构造器也可以是默认构造器。==
在派生类构造器中必须显式调用超类的构造器
==派生类在构造的时候并不会自动调用父类构造器,除非你愿意使用默认构造器(将成员初始化为0、false、null)==
方法需要一个父类参数的时候,可以传递之一个子类参数
多态和动态绑定
一个对象变量可以表示多种不同实际类型,即为多态。多态:(在继承树中)多个不同的类使用自己的代码来完成同一个方法,即同一个方法在继承树中的各个类有自己的执行代码。多态的时候,子类如果使用了父类的方法,则此时不能使用自己类内的属性,只能使用所在类的属性,因为此时动态绑定到了父类。
多态:一个名称的方法,在不同类中有同名的方法但不同的代码。
==通过父类可以使用子类的方法==
==*子类可以当做父类使用*==
==子类不能访问父类的private域==
C++中动态、动态绑定要求方法被声明为虚方法,而Java中默认就是虚方法,可以直接进行动态绑定,如果不想要动态绑定,可以使用final标记
将虚方法(多态覆盖方法)的返回类型定义为子类类型使该方法具有更高兼容性
允许类方法将自己的返回值类型修改为子类类型(即不返回自己的类型而是返回自己的子类的类型),这样,这个函数的返回值不仅仅可以给该类接收,还可以给子类类型的实例接收,对方法可用范围、兼容性有提高,是一种优良的设计。
//假设已有继承链:Father-->Child;Child类继承了Father类
/*以下为Father类内的方法*/
public Father getData()
{
//返回Father,不能用Child类接收
}
/*Child类内方法*/
public Child getData()
{
//返回Child类,此时不论是Child还是Father都能接收这个函数的返回值
}
==当子类覆盖父类的方法的时候,应该将父类方法的返回值从返回父类改成返回子类,这样可以提高方法的可用范围==
==注意动态绑定搜索合适的方法的时候会带来额外开销==
final方法/类
将类定义为final不仅禁止用户继承final类,还能确保有且只有一个使用这个类名的类,确保引用的这个类一定是这个final类。
==注意:动态绑定确实会带来额外开销,而将类方法标记为final可能可以让编译器提供优化,表示这是一个不会被覆盖的方法,允许编译器考虑是否需要内联优化。*当方法是覆盖方法时(即被override之后),编译器无法进行内联优化。*==
类类型的强制类型转换
子类可以直接当做父类使用,而父类转换为子类的时候必须进行类型转换。
只能在继承层次内进行对象的类型转换,在转换之前必须使用instanceof进行检查,如果不检查,转换失败将跑出异常。
==尽量不使用类型转换。==
C++:
Child *child = dynamic_cast<Child*>(father);
abstract抽象类/抽象方法
当一个类包含抽象函数时,类也必须声明为抽象的。
即使类不含抽象方法,也可以将类声明为抽象类。
抽象类不能实例化。
可以使用抽象类实例绑定一个非抽象类实例。
C++表示抽象类、纯虚函数(==含有纯虚函数的类即为抽象类==)
class Foo
{
public:
vitrual int func() = 0; //使用virtual和=0声明纯虚函数
}
Java的protected允许包内的其他类访问
==Java的protected的安全性比C++的差一点:Java的protected不仅对所有子类可访问,而且让同一个包内的所有其他类都可见。==
注意:什么都不写并不是默认protected,而是默认本包内可见,但是子类并不一定可见(本包内的子类可见,包外的子类不可见。)
Object类:所有类的超类
每个类都是Object派生而来的;若一个类没有明确的指出派生自哪个类,则它就默认为派生自Object,Object就是它的父类。
使用Object实例来引用任何类型的对象
Object obj = new Foo(10);
不能直接通过Object实例来调用实际对象类型的方法,除非进行类型转换
Foo f = (Foo)obj; //调用其方法的时候还必须使用原类型,不能直接通过Object来调用
Java中int、char、boolean不是对象,其他都是对象
Object与C++中void *的共同点
C++中所有的指针都可以转换为void *。
getClass()
得到实例所述的类。
equals(Object)方法
在自己的类中设计equals()方法的时候可以在自己的方法中调用这个方法。
使用@Override在参数不满足重载条件的情况下强制进行重载
方法equals(Foo)并没有覆盖(override)Object.equals(Object),因为equals()的参数类型是Object,并不是Foo,因此只是实现了重载而不是实现覆盖重写,override要求参数类型相同。
@Override public boolean equals(Object other)
//显式地指示出这个函数被重写,即使重写的函数参数不是Object
hashCode()和hash()
返回根据对象到处的hash值(int,可能小于0);hash()接收多个参数并对它们分别调用hashCode()后组合返回。
toString();由Object类提供toString()
getClass().getName()可以得到类的名称的字符串。
将一个对象与String使用‘+’连接起来,则编译器会自动地调用该对象的toString方法。
自动调用toString()
- “”+x可以自动调用,即空串与x连接,调用x.toString();
- System.out.println(obj);,会自动调用toString()然后输出结果String;
数组调用toString()并不会得到数组内容,应该调用Array.toString()或Arrays.deepToString()
==建议为每一个自己设计的类实现toString()功能==
泛型数组列表ArrayList<>
ArrayList<Foo> arr = new ArrayList<Foo>();
ArrayList<Foo> arr = new ArrayList<>(); //JavaSE7可以省略尖括号内的泛型类型
ArrayList<Foo> arr = new ArrayList<Foo>(10); //括号内的构造器参数表示初始容量
旧版本Java使用Vector而不是ArrayList。
方法
- add()
- ensureCapacity()
- size() //返回元素数目,不是元素数目乘以元素大小
- 等价于数组的length();
- trimToSize() //确定元素个数不再需要改变的时候,调用此方法以回收垃圾。
- set(index,value)
- add()
- get()
==注意:C++中的vector使用‘=’将发生拷贝构造:调用vector的拷贝构造函数并拷贝所有成员;而Java中是引用拷贝,‘=’将左侧引用绑定到与右侧引用相同的对象,实际只拷贝了对象的引用(即Vector变量)而没有拷贝Vector。==
==Java的泛型类型不允许是基本类型而必须是对象类型:如ArrayList<>尖括号内不允许使用基本类型而必须使用对象类型==
对象包装器Warpper与自动装箱/拆箱
Java中int、char、boolean等类型不是对象,有时候需要将它们转换为对象,即使用对应的类(Integer、Long、Float、Double、Short、Byte、Character、Void、Boolean)(前6个派生自Number超类)来表示它们,这些类就是包装器。
==把基本类型包装到一个对象,就是包装器。==
包装器的特点
- 一旦构造了包装器,则其内包装的值是不允许更改的。
- 包装器类是final类,不允许被继承,不允许定义它的子类。
自动装箱/拆箱
对ArrayList<包装器>的add(基本类型)调用会自动将基本类型转换为对应包装器后add;arrList.add(10);会自动转为arrList.add(Integer.valueOf(10));
自动拆箱:将包装器赋值给基本类型的时候回自动拆箱:int n = arrList.get(index)–>int n = arrList.get(index).intValue();
‘==’判断是否同一个对象;equals()判断是否相等
令方法支持可变参数(参数数量可变)
格式:类型名称+…:Type…;//Type可以使基本类型。
常用的为:Object… 、 int…、 Integer… ;
Type…等同于Type[],在方法内可以使用foreach来访问这些参数。
枚举类
==注意:枚举类是一种类类型,不是Java内置的基本类型。==
public enum Size { SMALL,MEDIUM,LARRGE,EXTRA_LARGE}; //花括号内是四个枚举类型的实例
/*可以为枚举对象添加构造函数和方法*/
public enum Size
{
SMALL("S"),MEDIUM("M"),LARGE("L"),EXTRA_LARGE("XL");
privete String size;
privete Size(String size) //私有构造函数
{
this.size = size;
}
public getSize()
{
return this.size;
}
}
- 使用’==’而不需要equals来比较枚举实例
- 所有枚举类型继承自Enum
- Enum的重要方法:toString()、valueOf()、values()、ordinal()返回枚举常量在enum声明中的位置,从0开始。
反射reflection
分析类能力的程序称为反射。
Class类
Java运行时系统为每个对象维护着运行时的类型标识和类的信息,可以通过对应的Class类来访问这些信息。一个Class类对象标识一个特定类的属性。如Object.getClass()得到Class类,Class.getName()得到类名,Class.forName(name)根据类名得到对应的Class对象。
任意Java类型或void,可以通过T.class得到对应的Class类;==int不是类,但int.Class是类。==
Class.forName()的用途
Class.forName()根据类名得到类对象,它可以用来手动加载指定的类。因此,可以在程序启动的时候显示欢迎界面,然后使用forName()来手动加载所需要的类,防止程序启动时很多类。总之就是利用forName()来加载类。
可以让一个实例.getClass()与类名称.Class比较以判断类型
==注意:类名.Class并没有括号结尾==
Class.newInstance()
根据Class来动态地创建新的类实例。
String class_name = "java.util.Random";
Object class_instance = Class.forName(class_name).newInstance(); //使用默认构造器构造
C++中的’反射’–RTTI运行时类型识别
Class类类似C++的type_info类。
getClass()类似C++的typeid运算符。
利用反射来分析类的能力
java.lang.reflect包中的三个类:Field、Method、Constructor。
可以用来查看、分析类的属性、方法和构造器。
P195
异常
异常分为已检查异常checked和未检查异常。未检查异常不会查找是否有异常handler,因此,不可能捕捉未检查异常,一旦遇到未检查异常,程序只能立即中止。
try
{
code_that_may_throws_exceptions();
}
catch(Execption e)
{
exception_1_handler();
}
使用java.lang.Throwable.printStackTrace()可以打印出栈的轨迹
该类/方法被Exception继承了,可以给Exception调用,可以在try-catch中调用。
反射可以用于在运行时动态地调用任意的方法
使用method对象实现方法指针
P208;这种方式容易出错,一般少用发方法指针。
java.lang.reflect.Method.invoke()
接口
接口用于描述类具有的功能,是函数接口原型
关键字:implements
==注意,接口不是类==
1. 接口的所有方法默认为public,可以省略public的声明
2. 接口中允许定义常量,但是绝对不允许有实例域,即不能有数据成员
3. 接口默认了public,但是接口方法实现的时候不能省略public,否则就会认为是包可见性,因为没有任何权限指示符
4. 实现接口的时候可以将接口实现为普通继承,参数为Object,可以实现为范型,此时直接使用范型类型而不需要从Object中转
5. 不能构造接口对象,但是可以声明接口对象变量,并且只能绑定实现了该接口的对象实例
6. 可以使用instanceof来检查一个类是否实现了一个接口
7. 接口也可以被继承
8. 在接口中定义的变量会自动变成public static final,即只允许定义常量。
9. 一个类可以实现多个接口,各个接口之间使用逗号分隔
java.swing.Timer定时器
使用这个定时器类,要求调用者实现了接口java.awt.event.ActionListener(),因为定时时间到达后调用的回调函数就是这个函数。(P222)
lambda表达式
lambda表达式:与C++的lambda表达式的名字和作用一模一样,用法格式似乎也很相似。
格式
(T arg1,T2 arg2) -> {arg1.foo();arg2.bar();}
某些情况下某些符号(参数类型、返回值声明、花括号)等可以省略(由编译器自动推断)。格式类似C++。
用途
作为一个代码块可以调用,可以在回调函数中填入,相当于回调时使用的代码块。
Java作为完全面向对象的语言,存在其不方便的地方:当需要回调或需要调用一个代码块的时候,往往需要传递一个对象,当需要回调函数的时候需要构造对应的对象,不那么方便,此时lambda即可解决这个问题。这时,==lambda表达式类似函数式编程的习惯,直接对一个代码块进行调用。==
函数式接口
只有一个抽象方法的接口即为函数式接口。需要这种接口的对象时,可以提供一个lambda表达式。
==可以简单地将函数式接口理解为能够接收lambda表达式的接口类对象,它只有一个方法(还必须是抽象的)==。
方法引用
如果lambda表达式仅仅只是将参数传递给另一个方法执行,那么可以直接使用这个方法的名称来替代lambda表达式,直接在原来写lambda表达式的地方写上方法名称即可。
方法引用:
System.out::println
/*等价于*/
x -> System.out.println(x);
方法引用的三种格式
object::instanceMethod //对象名::实例方法(对象就是类的实例)
Class::classStaticMethod //通过类名来调用静态方法
Class::instanceMethod //通过类名来调用实例方法,其中第一个参数表示调用方法的那个对象
例:String::toUpperCase等价(x,y) -> x.toUpperCase(y);
this::foo等价x -> this.foo(x); //此时第一个参数就是方法参数,因为this已经确定。
super::bar
构造器引用
冒号后面使用new,表示的就是构造器引用,与方法引用相似。
内部类
嵌套类和内部类格式相同,都是在一个类里面定义另外若干个类。
C++嵌套类
==嵌套是一种类之间的关系,而与对象实例没有关系。如:Foo包含嵌套类Bar和AClass,则Foo的实例foo并不包含子对象bar和a_class。==
嵌套类只说明类的关系,不表示对象实例的包含关系。
嵌套类的作用
- 命名控制
嵌套类的外部访问必须加上所属类的类名称作为命名空间。可以利用命名空间来与外部同名类进行区分。 - 访问控制
将嵌套类放在private:
下面声明就能限制外部引用了。
内部类
格式与嵌套类相同,功能稍微更强。
作用
内部类可以使用所在类的域,这样,当一个类需要使用另一个类的域时,可以将这个类声明为另一个类的内部类。
内部类的使用规则
- 当内部类希望引用外部类的域的时候,使用
OuterClass.this.domainName
来访问,注意this不可以缺少。 - 内部类实例化默认将this作为外部类。可以指定外部类,在实例化的时候使用outer_instance_name加英文句点来调用内部类构造(new),这时英文句点前面的实例就被内部类认为是外部类,而不再是this。
局部内部类
定义在方法里面的内部类就是局部内部类。当只需在一个方法中使用内部类的时候,就在这个方法里面把它定义为局部内部类。它仅仅只在本方法内可见,而且可以访问所在类的域和方法。
匿名内部类
没有名称的内部类。
静态内部类
即将内部类声明为static。与其他static一样,static不属于类的实例,不能访问类的各个域,因此,当仅仅只是想隐藏内部类而内部类不需要访问外围类的时候,可以声明为static。
代理
用于运行时动态定义新的类。
异常
异常处理用于解决程序运行时遇到不可解决的错误的时候。
==注意:方法抛出异常后,方法不能再继续执行,而是立即退出,因此没有返回值==。
所有异常对象实例都是派生于throwable类的。==throwable派生了Exception和Error。== 当抛出Error时,表示资源耗尽或内部错误,一般这种错误是无能为力的,无法解决(一般不是代码问题)。
注意:throws xxException的声明是在方法那里写的,而不是对类的描述。即,==抛出异常的描述是对方法的描述,不是对类的描述。==
Exception的两个分支
- 由于程序错误导致的异常,即为RuntimeException。
- 不是程序错误,而是其他问题如I/O错误,硬件工作不正常,文件不存在等,即为其他异常。
==注意:以上各异常的分类是继承于这些异常的。==
==Error和RuntimeException都是不可检查的异常,因此它们不能够被捕捉。==
C++异常
C++有两种异常:logical_error(相当于Java的RuntimeException),runtime_error(其他异常)。
==注意:如果不捕捉异常,则会导致对应的线程终止,不是进程终止==。
throws抛出是指抛出异常到方法之外,如果异常在方法内捕捉,则方法不需要throws声明
==在方法内进行try-catch就是不让异常被抛出到方法之外导致线程退出。==
如果超类方法没有抛出异常,则覆盖它的子类异常也不能够抛出异常。另外,子类覆盖方法抛出的异常要比覆盖的超类方法更细、更特定准确,或者干脆不会抛出异常。==当一个类方法会抛出异常时,它实际上可以抛出这个异常类的其中一个子类==。
C++抛出异常
- C++的抛出异常throw说明符在运行时执行,Java是在编译时执行。
- C++函数如果抛出了没有在throw列表中声明的异常,则会调用unexpected函数,默认终止程序。
- C++中如果没有throw则程序可能抛出任何异常,而Java不声明的话则不能抛出任何可受查异常。
使用throw抛出异常
使用throw抛出异常,要求抛出的实例必须是Throwable的子类,且必须使用new进行实例化,或者是使用
xxxException e = new xxxException()
后throw e
抛出。
C++可以抛出任意类型值而Java只能抛出Throwable的子对象实例
自定义新的异常类
创建异常类,从一个已有的合适的异常类继承即可。
Throwable()
构造异常类的时候,或使用异常类的时候,将异常信息String作为参数传递给new构造器就能传递异常描述信息,使用getMessage()可以获得描述信息。==自定义的异常类也可以这样,定义完默认构造器后,定义一个String参数的构造器,将参数直接传递给super()能实现上面同样的功能。
捕获异常
对于GUI程序,出现异常而未捕捉的时候,程序会结束异常线程并且会返回到GUI处理循环中,并不会导致GUI程序退出。
try
{
...
}
catch(xxxException e)
{
...
}
注意:==当try中出现的异常被catch捕捉后,try后面的代码在catch执行完成后并不会继续执行,也就是说被捕捉的异常后面的try里面的代码将不会执行==。
throws声明和try-catch语句块的联系
throws ExceptionTypeName
声明表示该方法会抛出异常给调用者(即抛出的异常并没有在方法内捕捉,而是让这个异常传递
到了方法外)。throws表示调用者需要处理这个异常,不写throws或没有出现在throws里面的异常并不是不会产生,要么这种异常不会发生,要么这种异常在方法内部被捕捉处理了(即不会抛出到方法外面,因为被捕捉了)。- try-catch表示将异常捕捉,因此这个被捕捉的异常并不会抛出到方法外面(不会传递到方法之外,不会传递给调用者)。==当某个异常由方法自己处理的时候,不需要在throws中声明这种异常(当然,用try-catch把它捕捉);当异常希望传递给调用者处理的时候,不要捕捉它,并在throws中写出这个异常类型,即可将它传递给调用者让调用者来处理。
注意:有些情况下应该由方法来处理异常,有些情况下应该由调用者来处理异常。如:文件不存在这种异常,应该给调用者来处理,这样,直接抛出这个异常到方法外(throws)能让调用者清楚地直到问题所在。而有些异常需要方法自己来处理(一般是方法自己的问题或自己能解决的问题)。根据实际情况决定是否需要捕捉,还是将它传递给调用者。
总结:==一种异常,如果被try-catch了,那它就不需要throws声明;如果没有被捕捉,那么就必须要throws通知编译器和调用者可能会出现这种异常。
注意C++捕获异常的时候对类实例参数采用引用传递较好:catch(Exception& e)
注意事项
==一个子类覆盖方法,其throws说明符中出现的异常种类不能超过父类方法的范围。即,父类不会throws的异常,子类也不能throws==。这样,当父类没有thorws任何异常的时候,子类也不能throws任何异常,而必须捕捉所有可能的异常。
在一个/多个catch()钟捕获多个异常
捕获多个异常可以使用多个catch()
可以在一个catch中捕获多个异常,这表示对于这多个异常有相同的catch处理块
try
{
...
}
//格式:是类名称进行'|'而只含一个变量实例名称e
catch (FileNotFoundException | UnknownHostException e)
{
...
}
==捕获多个异常时,异常对象变量隐含为final==。
==允许在catch中再次抛出异常,这样做的目的是改变异常的类型、包装异常、处理一些工作后再抛出异常或记录异常的信息(log)后再抛出。==
finally子句
try
{
...
} //可以没有catch{}而只有finally{}
finally
{
...
}
- 不论有没有发生异常、异常是否被捕捉,在try-catch结束后、方法退出返回(没有异常、正常return)、或转交给异常处理前都会执行finally子句。
2.finally如果使用了return来返回值,会覆盖掉try中返回的值,因为finally总是在方法退出之前执行。
带资源的try(Resource …){}
try(Scanner in = new Scanner(new FileInputStream("./test.txt"),"UTF-8");
PrintWriter out = new PrintWriter("out.txt"))
{
...
}
finally
{
//可以有finally
}
==不论这个块是怎么退出的,这个资源都会关闭;finally子句在资源关闭后才会执行==。
注意:要求资源实现了AutoCloseAble接口类(一般内置的资源类都实现了),资源关闭执行的是Resource.close()。
断言
==断言结果错误时(False),则视为发生了不可恢复的、致命的程序设计错误,抛出error异常,仅用于程序测试阶段,程序上线后不能使用断言,只允许使用异常和log==。
assert condition; //不需要括号
assert condiction : expression; //表达式被用于转换为一个消息字符串
/*若断言为False,则抛出AssertionError异常*/
注意:C语言的assert宏会将条件转换为字符串,因此在断言结果中可以看到条件;而Java想要看到条件则需要显式地将条件作为冒号后的字符串传递。
使用-enableassertions或-ea启用断言
断言是类加载器控制的,当没有启动断言的时候,类加载器并不会加载类里面的断言,因此,断言在默认情况下并不占用资源、并不影响运行速度。断言开启后,与try-catch一样,有额外开销。
==有些类由类加载器加载,有些则是虚拟机直接加载。==
禁用:-da、-disableassertions
对于系统类:-esa、-enablesystemassertions
使用-ea加冒号表示指定某些类或包开启断言
日志
Logger:全局日志记录器,有7个日志级别。
Logger.getGlobal(); //得到全局记录器
Logger.getLogger("com.company.project"); //创建或取得记录器
==创建或取得记录器实例的要求:必须将记录器实例对象声明为static final,否则可能会因为未被引用而导致被垃圾回收==。
可以添加自定义的日志过滤器和格式化器
lint
lint最初是C语言程序用来定位潜在问题的工具,现在用来表示用于查找可疑但是不违反语法规则的工具。
泛型
对多种不同类型编写一套通用的代码,使这些不同的类型可以重用这段代码。
类型参数
在泛型对象中的尖括号<>
里面填写的class类型名称就是类型参数。
ArrayList<String> strings = new ArrayList<>();
//可以省略new后面的类型参数,注意不要忘了()表示方法调用
Pair<String,String> aPair = new Pair<>("foo","bar");
定义泛型类
public class Foo<T>
{
private T data;
...
public Foo()
{
...
}
public Foo(T something)
{
...
}
}
在普通类中定义泛型方法
class AClass
{
public static <T> T func()
{
...
}
}
方法返回值中的类型参数用于表示方法参数,在==调用的时候向尖括号内传入类型参数,大多数情况下这个类型参数可以省略,因为编译器可以推断出来==。最好显式写出来。
调用:
String ret = aInstance.<String>func();
C++泛型调用格式
C++中将类型参数置于方法名称和参数列表之间:
func<T,U>(args);
只允许某些实现了指定接口的对象调用泛型方法
public static <T extends Comparable> T getMin(T[] a);
这样,只允许实现了Commparable
接口的类调用这个泛型方法。
==注意:虽然尖括号内限定的是实现了哪种接口,而不是继承自哪些类,但是不是使用implements却是使用extends,这与继承和实现接口的关键字并不对应。==
==当希望有多个限定的时候(即调用者必须将这些接口、类都实现、继承),多个限定使用&
来分隔。
将泛型类型的类型参数去掉,即将尖括号和其内包含的类型参数去掉,得到的就是原始类型。在Java虚拟机中,实际上并不存在泛型类型,所有的类型都是普通类型。虚拟机中并没有泛型类型和方法,只有普通类型和方法。
==不支持泛型类型的数组==。
泛型类的类型参数实际上会被’擦除’掉
使用’?’表示类型参数通配符
Pair<? extends Foo>
使用通配符后,允许类型参数发生变化。
这时,如果Bar是Foo的子类,那么可以传递Bar类型作为类型参数,如果没有使用?
通配符,则只能传递Foo类型作为类型参数。
使用’?’加super限定只接受限定类的超类
使用单个’?’表示无限定通配符
类型参数被擦除,因此泛型类型的反射得到的信息比普通类型较少
集合
队列
==可以使用循环数组或链表实现==。
ArrayDeque();
LinkedList();
可以使用它们的接口类Queue<>
来作为变量引用它们。
Queue<String> str_queue = new LinkedList<>(10); //size == 10
使用集合类型的接口类对象变量来接收对象更佳
各种集合类型分别有各自的几种实现方式,它们都实现了同一个接口对象(如:循环数组实现的队列和链表实现的队列都实现了Queue<>接口)。
当使用它们的接口类型对象变量来接收这些集合类型实例的时候,并在后面的代码中使用这个对象变量表示集合实例。这样做的一个好处是,由于使用了基类来表示子类实例,因此存在多态。当程序想要从一种实现转而使用另一种实现时(如:本来使用链表队列,转而使用循环数组队列),使用接口类对象变量的代码只需要修改实例化的对象,而不需要修改其他代码。(如果不这样,则将LinkedList<>修改为ArrayList<>的时候,代码中所有的LinkedList都必须替换为ArrayList,极难维护。)
==应该使用接口类或基类来接收实例化的对象更佳,运用到了多态的优点==。
Collection接口
所有集合类的基本接口是Collection
Collection.iterator()得到对应的迭代器
迭代器对象实现了接口Iterator
Iterator接口
包含方法next()、hasNext()、remove()、foreachRemaining()。
注意:==在next()之前必须使用hasNext()判断,因为到达集合末尾的时候next()会抛出异常NoSuchElementException==。
使用foreach比较简洁,可以不使用hasNext判断,但实际编译器会将其翻译为使用hasNext判断,实际执行情况相同
foreachRemaining()
它自动循环遍历集合中的每一个元素,并且根据传递的lambda表达式对每个元素执行这个lambda,相当于对每一个元素执行一个指定的回调函数。
Java迭代器与C++的不同
C++的迭代器可以通过加和减来实现移动操作,且可以将迭代器当做数组索引来取得对应的元素。而Java的迭代器仅仅只能通过next()来移动,并不能对迭代器直接操作。实际上,next()的作用是查询下一个元素,而副作用是另迭代器向下走一步。
Java迭代器位于两个元素中间
应该将Java迭代器视作位于两个元素之间,调用next()使迭代器越过下一个元素到达下两个元素之间,并且返回越过的那个元素的引用。
Iterator.remove()
将会删除上一次next()
返回的元素。
有且只有next()可以实现迭代器移动,remove()并不能自动移动迭代器
特别注意:==仅仅只有next()能够移动迭代器,而remove()并不能移动迭代器!这意味着不能多次连续调用remove()来连续删除元素,而必须让remove()和next()间隔调用,否则会抛出异常!==
remove()不能移动迭代器。无论如何,每一次remove必须至少伴有一个next(),当然,每一次的next()又要求首先判断hasNext()
与C++的不同
C++不仅能够通过加和减来移动迭代器,而且,remove()能够自动移动迭代器而不需要用户手动移动迭代器。
remove(Object)删除指定的元素,有匹配的元素被删除时返回true,这个重载不依赖next()
集合的两个基本接口:Collection和Map(映射)
==对于映射:需要使用put()
和get()
来读取,而不是使用add()
和next()
==。
对于Map,使用get()访问而不是Collection()的next()。
List
是一个有序集合;元素的存储位置是有规律的;可以使用迭代器访问,也可以使用
get()
实现按索引的随机访问。
List使用的迭代器不是Iterator()而是ListIterator()。
==List也有数组和链表两种实现,都支持随机访问,但是链表实现的随机访问很慢。可以检测标记接口if( a_list instanceof RandomAccess)
来检测实现是否支持快速随机访问。==别忘了标记接口的作用:实际并不包含接口方法。只是象征地向用户或编译器说明一些类的特性。
Set:集,元素有序的Collection
库中的具体集合
- ArrayList
- LinkedList
- ArrayDeque
- HashSe–无序:并不是带有Set就一定有序。
- TreeSet
- EnumSet
- LinkedHashSet–记住元素插入顺序
- PriorityQueue
- HashMap
- TreeMap
- EnumMap
- LinkedHashMap
- WeakHashMap
- IdentityHashMap
Java链表都是双向的,不存在单向链表
反向遍历链表的时候,remove()删除刚刚遍历过的那个元素,而不是下一个元素。
注意:链表是有序的,是指它保持了插入顺序而不是指有排序顺序
==具有插入顺序即为有序,并不是必须满足字典序、大小序才是有序。==
==对于所有集合:当一个迭代器add/remove之后,另一个迭代器的next()将会抛出异常。一般允许有多个读迭代器,但只允许有一个写迭代器。==
==××注意:对链表的set(即修改元素的值而不增加、减少元素个数)并不会导致其他迭代器抛出异常××==。
List有一个listIterator(),iterator()和listIterator()都能用
==有两种访问元素的方法:1. 迭代器;2. get/set();第二种方法对于List来说开销比较大,但是有这种实现的API提供。
在单线程或不需要同步的多线程的时候使用ArrayList,多线程且需要同步的时候使用Vector
注意区分集Set和集合Collection
==注意常数的类型转换和变量的类型转换的区别:常数的类型转换使用后置转换:如将1.2转为float应该使用2.2F,而对于double才是使用(float)。==
注意:Java中所有的浮点常数都是double,除非加了后缀F,因此将浮点数赋值给float是会编译错误的。
2017年10月07日10:59:34
JAR文件
JAR即为Java归档。将工程中的各种文件和目录打包并压缩,最终提供的软件只需要一个文件即可。==压缩采用的格式为ZIP==。
JAR工具
jdk/bin目录下的jar工具
jar cvf JARFileName SourceFile1 Dir1 SourceFile...
jar cvf HelloWorld.jar ./*.class ./*.gif
//类似于UNIX的tar命令
常用参数:
-c 创建一个归档,打包文件的时候使用
-f 指定压缩得到的目标文件名或被解压的文件的文件名,如果不指定,则命令会从标准输入输出中读取/写入
-v 要求命令打印出其执行的详细操作、结果
-x 解压文件;当有gz后缀时,在x前面加上’z’
0 存储文件,但是不进行压缩
-e 指定程序入口点(指定程序的主类)
启动JAR打包的Java程序
java -jar ApplicationName.jar
在Windows中可以使用包装器来将JAR程序转换为windows可执行程序
当然,要求windows包含JVM。