==~/.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
>>
没有指定执行的是算数还是逻辑右移,在执行的时候回采用其中一种效率较高的右移;==在Java中使用>>
将使用符号位填充最高位(即逻辑右移);使用>>>
使用0来填充最高位(即算术右移)==;class_type instance_name
的形式来声明对象,如:C++可以使用QLabel label的形式来声明实例,但是Java不行,==Date date==并没有声明实例,要么用=new Date(),要么用另一个同类型实例拷贝给它(引用拷贝)foreach
参考了C#,所以并不能老是觉得C#山寨了Javajavac foo.java
得到foo.class,运行使用java foo
而不是java foo.class; ==运行时,只需要提供类名而不要带上任何扩展名==
- \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
java
String.join('/','S','M','L','XL');
//"S/M/L/XL"
equals()
,但是一般不使用compareTo()
==注意: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];
int[] arr = {1,2,3};
arr = new int[] {4,5,6};
double[] tmp = arr[0];
arr[0] = arr[1];
arr[1] = tmp; //利用行的引用交换实现多维数组行的交换
import java.util.Arrays
不规则数组即每一行的大小不同
int [][]odds = new int[MAX+1][];
for(int n=0;n <= MAX;++n){
odds[n] = new int[n+1]; //第n行有n+1个元素
}
double[][] arr = new double[10][10];
相当于C++:
double** arr = new double*[10];
- 对象的行为
- 对象的状态
- 对象的标识(身份、识别)
Math::PI
Math.PI
格式:this(args);
例:this("hello",10);
这样可以调用另一个构造器,对于==C++,不能调用其他构造器,只能把构造器中重复的代码封装到一个方法再在构造器中调用==
==所有的类都来自于超类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次实例化只在第一次执行这个静态初始化块,而对象初始化块则每次实例化都会执行==
但是最好不使用它,毕竟书上没有给出格式和方法,资源最好在用完就释放,如文件描述符或句柄。
main()方法是静态的,因为它并不依赖对象进行操作
====*在一个模块/类中增加main()方法可以实现对这个类/模块的单元测试*====
对于工程内的一个模块,可以对其使用一个main()来编写一个单元测试而不影响整个系统的功能,即测试代码和功能代码可以共存。
原理是:一个工程的主模块中的main()作为系统的启动,而此时单元模块没有main(因为它并不是主模块),只有本类的构造器。
当需要对这个模块:类进行单元测试的时候,就对这个类新增一个main()方法,测试代码写在main()内,并使用javac和java命令来编译执行这一个模块,这时该模块可以独立运行单元测试,因为它拥有了自己的main()方法。同时,它并不影响已有系统的编译和运行,因为工程只是使用了类的构造器,并不关心类内的main()
将这些基本类型的数据成员封装到一个类中,这样比较清晰
import java.util.*;
使用Scanner的一个实例关联到System.in标准输入流后才能读取输入,并不能直接从System.in中直接读取
Scanner类不能禁止回显,因此输入的密码永远可见。
必须使用Console类:它可以使用readPasswd()来读取密码并禁止回显。
==*特别注意:使用readPasswd()得到的密码,为了安全,必须使用cha[]一维数组来接收,不要使用String!*== 因为String可以被共享且String不能修改,因此可能会被读取到。另外,密码数组不使用后,必须用各种值填充之,以抹去密码。
System.out.printf()沿用了C语言的printf(),并且新增了一些%标志
同样使用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()不可或缺==
==使用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;
for(int iter : names) //注意:不是foreach(:)
System.out.println(names[iter]);
==处理多维数组必须使用嵌套的foreach()语句==
for(double[] rows : arr)
for(double value : rows)
System.out.println(value);
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
public class Foo extends Bar
{
}
==注意:Java仅仅只有公共继承,并没有私有继承和保护继承==
==特别注意:子类并不能访问父类的private域,而只能通过父类给出的接口来访问。注意,private只允许本类可以访问,即使是派生的子类也不允许访问父类的私有域==
super.foo();
C++:
BaseClassName::foo();
注意使用超类名而不是实例名,因为它仅仅只是指示编译器调用父类方法,并不是通知编译器使用另一个实例的方法
super(args);
Foo::Foo(string str,double num) : Bar(int index)
{
//...构造函数主体
}
==利用父类构造器完成对父类私有成员的初始化,毕竟子类没有办法访问父类私有成员==
在==Java中,没有任何参数的构造器即为默认构造器,在C++中,不仅没有参数的构造器是默认构造器,所有参数都有默认值的构造器也可以是默认构造器。==
==派生类在构造的时候并不会自动调用父类构造器,除非你愿意使用默认构造器(将成员初始化为0、false、null)==
一个对象变量可以表示多种不同实际类型,即为多态。多态:(在继承树中)多个不同的类使用自己的代码来完成同一个方法,即同一个方法在继承树中的各个类有自己的执行代码。多态的时候,子类如果使用了父类的方法,则此时不能使用自己类内的属性,只能使用所在类的属性,因为此时动态绑定到了父类。
多态:一个名称的方法,在不同类中有同名的方法但不同的代码。
==通过父类可以使用子类的方法==
==*子类可以当做父类使用*==
==子类不能访问父类的private域==
允许类方法将自己的返回值类型修改为子类类型(即不返回自己的类型而是返回自己的子类的类型),这样,这个函数的返回值不仅仅可以给该类接收,还可以给子类类型的实例接收,对方法可用范围、兼容性有提高,是一种优良的设计。
//假设已有继承链:Father-->Child;Child类继承了Father类
/*以下为Father类内的方法*/
public Father getData()
{
//返回Father,不能用Child类接收
}
/*Child类内方法*/
public Child getData()
{
//返回Child类,此时不论是Child还是Father都能接收这个函数的返回值
}
==当子类覆盖父类的方法的时候,应该将父类方法的返回值从返回父类改成返回子类,这样可以提高方法的可用范围==
==注意动态绑定搜索合适的方法的时候会带来额外开销==
将类定义为final不仅禁止用户继承final类,还能确保有且只有一个使用这个类名的类,确保引用的这个类一定是这个final类。
==注意:动态绑定确实会带来额外开销,而将类方法标记为final可能可以让编译器提供优化,表示这是一个不会被覆盖的方法,允许编译器考虑是否需要内联优化。*当方法是覆盖方法时(即被override之后),编译器无法进行内联优化。*==
子类可以直接当做父类使用,而父类转换为子类的时候必须进行类型转换。
只能在继承层次内进行对象的类型转换,在转换之前必须使用instanceof进行检查,如果不检查,转换失败将跑出异常。
==尽量不使用类型转换。==
C++:
Child *child = dynamic_cast(father);
当一个类包含抽象函数时,类也必须声明为抽象的。
即使类不含抽象方法,也可以将类声明为抽象类。
抽象类不能实例化。
可以使用抽象类实例绑定一个非抽象类实例。
class Foo
{
public:
vitrual int func() = 0; //使用virtual和=0声明纯虚函数
}
==Java的protected的安全性比C++的差一点:Java的protected不仅对所有子类可访问,而且让同一个包内的所有其他类都可见。==
注意:什么都不写并不是默认protected,而是默认本包内可见,但是子类并不一定可见(本包内的子类可见,包外的子类不可见。)
每个类都是Object派生而来的;若一个类没有明确的指出派生自哪个类,则它就默认为派生自Object,Object就是它的父类。
Object obj = new Foo(10);
Foo f = (Foo)obj; //调用其方法的时候还必须使用原类型,不能直接通过Object来调用
C++中所有的指针都可以转换为void *。
得到实例所述的类。
在自己的类中设计equals()方法的时候可以在自己的方法中调用这个方法。
方法equals(Foo)并没有覆盖(override)Object.equals(Object),因为equals()的参数类型是Object,并不是Foo,因此只是实现了重载而不是实现覆盖重写,override要求参数类型相同。
@Override public boolean equals(Object other)
//显式地指示出这个函数被重写,即使重写的函数参数不是Object
返回根据对象到处的hash值(int,可能小于0);hash()接收多个参数并对它们分别调用hashCode()后组合返回。
getClass().getName()可以得到类的名称的字符串。
将一个对象与String使用‘+’连接起来,则编译器会自动地调用该对象的toString方法。
==建议为每一个自己设计的类实现toString()功能==
ArrayList arr = new ArrayList();
ArrayList arr = new ArrayList<>(); //JavaSE7可以省略尖括号内的泛型类型
ArrayList arr = new ArrayList(10); //括号内的构造器参数表示初始容量
旧版本Java使用Vector而不是ArrayList。
==注意:C++中的vector使用‘=’将发生拷贝构造:调用vector的拷贝构造函数并拷贝所有成员;而Java中是引用拷贝,‘=’将左侧引用绑定到与右侧引用相同的对象,实际只拷贝了对象的引用(即Vector变量)而没有拷贝Vector。==
==Java的泛型类型不允许是基本类型而必须是对象类型:如ArrayList<>尖括号内不允许使用基本类型而必须使用对象类型==
Java中int、char、boolean等类型不是对象,有时候需要将它们转换为对象,即使用对应的类(Integer、Long、Float、Double、Short、Byte、Character、Void、Boolean)(前6个派生自Number超类)来表示它们,这些类就是包装器。
==把基本类型包装到一个对象,就是包装器。==
对ArrayList<包装器>的add(基本类型)调用会自动将基本类型转换为对应包装器后add;arrList.add(10);会自动转为arrList.add(Integer.valueOf(10));
自动拆箱:将包装器赋值给基本类型的时候回自动拆箱:int n = arrList.get(index)–>int n = arrList.get(index).intValue();
格式:类型名称+…: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开始。
分析类能力的程序称为反射。
Java运行时系统为每个对象维护着运行时的类型标识和类的信息,可以通过对应的Class类来访问这些信息。一个Class类对象标识一个特定类的属性。如Object.getClass()得到Class类,Class.getName()得到类名,Class.forName(name)根据类名得到对应的Class对象。
任意Java类型或void,可以通过T.class得到对应的Class类;==int不是类,但int.Class是类。==
Class.forName()根据类名得到类对象,它可以用来手动加载指定的类。因此,可以在程序启动的时候显示欢迎界面,然后使用forName()来手动加载所需要的类,防止程序启动时很多类。总之就是利用forName()来加载类。
==注意:类名.Class并没有括号结尾==
根据Class来动态地创建新的类实例。
String class_name = "java.util.Random";
Object class_instance = Class.forName(class_name).newInstance(); //使用默认构造器构造
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();
}
该类/方法被Exception继承了,可以给Exception调用,可以在try-catch中调用。
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.awt.event.ActionListener(),因为定时时间到达后调用的回调函数就是这个函数。(P222)
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,表示的就是构造器引用,与方法引用相似。
嵌套类和内部类格式相同,都是在一个类里面定义另外若干个类。
==嵌套是一种类之间的关系,而与对象实例没有关系。如:Foo包含嵌套类Bar和AClass,则Foo的实例foo并不包含子对象bar和a_class。==
嵌套类只说明类的关系,不表示对象实例的包含关系。
private:
下面声明就能限制外部引用了。格式与嵌套类相同,功能稍微更强。
内部类可以使用所在类的域,这样,当一个类需要使用另一个类的域时,可以将这个类声明为另一个类的内部类。
OuterClass.this.domainName
来访问,注意this不可以缺少。定义在方法里面的内部类就是局部内部类。当只需在一个方法中使用内部类的时候,就在这个方法里面把它定义为局部内部类。它仅仅只在本方法内可见,而且可以访问所在类的域和方法。
没有名称的内部类。
即将内部类声明为static。与其他static一样,static不属于类的实例,不能访问类的各个域,因此,当仅仅只是想隐藏内部类而内部类不需要访问外围类的时候,可以声明为static。
用于运行时动态定义新的类。
异常处理用于解决程序运行时遇到不可解决的错误的时候。
==注意:方法抛出异常后,方法不能再继续执行,而是立即退出,因此没有返回值==。
所有异常对象实例都是派生于throwable类的。==throwable派生了Exception和Error。== 当抛出Error时,表示资源耗尽或内部错误,一般这种错误是无能为力的,无法解决(一般不是代码问题)。
注意:throws xxException的声明是在方法那里写的,而不是对类的描述。即,==抛出异常的描述是对方法的描述,不是对类的描述。==
==Error和RuntimeException都是不可检查的异常,因此它们不能够被捕捉。==
C++有两种异常:logical_error(相当于Java的RuntimeException),runtime_error(其他异常)。
==注意:如果不捕捉异常,则会导致对应的线程终止,不是进程终止==。
==在方法内进行try-catch就是不让异常被抛出到方法之外导致线程退出。==
如果超类方法没有抛出异常,则覆盖它的子类异常也不能够抛出异常。另外,子类覆盖方法抛出的异常要比覆盖的超类方法更细、更特定准确,或者干脆不会抛出异常。==当一个类方法会抛出异常时,它实际上可以抛出这个异常类的其中一个子类==。
使用throw抛出异常,要求抛出的实例必须是Throwable的子类,且必须使用new进行实例化,或者是使用
xxxException e = new xxxException()
后throw e
抛出。
创建异常类,从一个已有的合适的异常类继承即可。
构造异常类的时候,或使用异常类的时候,将异常信息String作为参数传递给new构造器就能传递异常描述信息,使用getMessage()可以获得描述信息。==自定义的异常类也可以这样,定义完默认构造器后,定义一个String参数的构造器,将参数直接传递给super()能实现上面同样的功能。
对于GUI程序,出现异常而未捕捉的时候,程序会结束异常线程并且会返回到GUI处理循环中,并不会导致GUI程序退出。
try
{
...
}
catch(xxxException e)
{
...
}
注意:==当try中出现的异常被catch捕捉后,try后面的代码在catch执行完成后并不会继续执行,也就是说被捕捉的异常后面的try里面的代码将不会执行==。
throws ExceptionTypeName
声明表示该方法会抛出异常给调用者(即抛出的异常并没有在方法内捕捉,而是让这个异常传递
到了方法外)。throws表示调用者需要处理这个异常,不写throws或没有出现在throws里面的异常并不是不会产生,要么这种异常不会发生,要么这种异常在方法内部被捕捉处理了(即不会抛出到方法外面,因为被捕捉了)。- try-catch表示将异常捕捉,因此这个被捕捉的异常并不会抛出到方法外面(不会传递到方法之外,不会传递给调用者)。==当某个异常由方法自己处理的时候,不需要在throws中声明这种异常(当然,用try-catch把它捕捉);当异常希望传递给调用者处理的时候,不要捕捉它,并在throws中写出这个异常类型,即可将它传递给调用者让调用者来处理。
注意:有些情况下应该由方法来处理异常,有些情况下应该由调用者来处理异常。如:文件不存在这种异常,应该给调用者来处理,这样,直接抛出这个异常到方法外(throws)能让调用者清楚地直到问题所在。而有些异常需要方法自己来处理(一般是方法自己的问题或自己能解决的问题)。根据实际情况决定是否需要捕捉,还是将它传递给调用者。
总结:==一种异常,如果被try-catch了,那它就不需要throws声明;如果没有被捕捉,那么就必须要throws通知编译器和调用者可能会出现这种异常。
==一个子类覆盖方法,其throws说明符中出现的异常种类不能超过父类方法的范围。即,父类不会throws的异常,子类也不能throws==。这样,当父类没有thorws任何异常的时候,子类也不能throws任何异常,而必须捕捉所有可能的异常。
try
{
...
}
//格式:是类名称进行'|'而只含一个变量实例名称e
catch (FileNotFoundException | UnknownHostException e)
{
...
}
==捕获多个异常时,异常对象变量隐含为final==。
==允许在catch中再次抛出异常,这样做的目的是改变异常的类型、包装异常、处理一些工作后再抛出异常或记录异常的信息(log)后再抛出。==
try
{
...
} //可以没有catch{}而只有finally{}
finally
{
...
}
- 不论有没有发生异常、异常是否被捕捉,在try-catch结束后、方法退出返回(没有异常、正常return)、或转交给异常处理前都会执行finally子句。
2.finally如果使用了return来返回值,会覆盖掉try中返回的值,因为finally总是在方法退出之前执行。
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想要看到条件则需要显式地将条件作为冒号后的字符串传递。
断言是类加载器控制的,当没有启动断言的时候,类加载器并不会加载类里面的断言,因此,断言在默认情况下并不占用资源、并不影响运行速度。断言开启后,与try-catch一样,有额外开销。
==有些类由类加载器加载,有些则是虚拟机直接加载。==
禁用:-da、-disableassertions
对于系统类:-esa、-enablesystemassertions
Logger:全局日志记录器,有7个日志级别。
Logger.getGlobal(); //得到全局记录器
Logger.getLogger("com.company.project"); //创建或取得记录器
==创建或取得记录器实例的要求:必须将记录器实例对象声明为static final,否则可能会因为未被引用而导致被垃圾回收==。
lint最初是C语言程序用来定位潜在问题的工具,现在用来表示用于查找可疑但是不违反语法规则的工具。
对多种不同类型编写一套通用的代码,使这些不同的类型可以重用这段代码。
在泛型对象中的尖括号<>
里面填写的class类型名称就是类型参数。
ArrayList strings = new ArrayList<>();
//可以省略new后面的类型参数,注意不要忘了()表示方法调用
Pair aPair = new Pair<>("foo","bar");
public class Foo<T>
{
private T data;
...
public Foo()
{
...
}
public Foo(T something)
{
...
}
}
class AClass
{
public static T func()
{
...
}
}
方法返回值中的类型参数用于表示方法参数,在==调用的时候向尖括号内传入类型参数,大多数情况下这个类型参数可以省略,因为编译器可以推断出来==。最好显式写出来。
调用:
String ret = aInstance.func();
C++中将类型参数置于方法名称和参数列表之间:
func<T,U>(args);
public static T getMin(T[] a);
这样,只允许实现了Commparable
接口的类调用这个泛型方法。
==注意:虽然尖括号内限定的是实现了哪种接口,而不是继承自哪些类,但是不是使用implements却是使用extends,这与继承和实现接口的关键字并不对应。==
==当希望有多个限定的时候(即调用者必须将这些接口、类都实现、继承),多个限定使用&
来分隔。
将泛型类型的类型参数去掉,即将尖括号和其内包含的类型参数去掉,得到的就是原始类型。在Java虚拟机中,实际上并不存在泛型类型,所有的类型都是普通类型。虚拟机中并没有泛型类型和方法,只有普通类型和方法。
==不支持泛型类型的数组==。
Pair extends Foo>
使用通配符后,允许类型参数发生变化。
这时,如果Bar是Foo的子类,那么可以传递Bar类型作为类型参数,如果没有使用?
通配符,则只能传递Foo类型作为类型参数。
==可以使用循环数组或链表实现==。
ArrayDeque();
LinkedList();
可以使用它们的接口类Queue<>
来作为变量引用它们。
Queue str_queue = new LinkedList<>(10); //size == 10
各种集合类型分别有各自的几种实现方式,它们都实现了同一个接口对象(如:循环数组实现的队列和链表实现的队列都实现了Queue<>接口)。
当使用它们的接口类型对象变量来接收这些集合类型实例的时候,并在后面的代码中使用这个对象变量表示集合实例。这样做的一个好处是,由于使用了基类来表示子类实例,因此存在多态。当程序想要从一种实现转而使用另一种实现时(如:本来使用链表队列,转而使用循环数组队列),使用接口类对象变量的代码只需要修改实例化的对象,而不需要修改其他代码。(如果不这样,则将LinkedList<>修改为ArrayList<>的时候,代码中所有的LinkedList都必须替换为ArrayList,极难维护。)
==应该使用接口类或基类来接收实例化的对象更佳,运用到了多态的优点==。
所有集合类的基本接口是Collection
包含方法next()、hasNext()、remove()、foreachRemaining()。
注意:==在next()之前必须使用hasNext()判断,因为到达集合末尾的时候next()会抛出异常NoSuchElementException==。
它自动循环遍历集合中的每一个元素,并且根据传递的lambda表达式对每个元素执行这个lambda,相当于对每一个元素执行一个指定的回调函数。
C++的迭代器可以通过加和减来实现移动操作,且可以将迭代器当做数组索引来取得对应的元素。而Java的迭代器仅仅只能通过next()来移动,并不能对迭代器直接操作。实际上,next()的作用是查询下一个元素,而副作用是另迭代器向下走一步。
应该将Java迭代器视作位于两个元素之间,调用next()使迭代器越过下一个元素到达下两个元素之间,并且返回越过的那个元素的引用。
Iterator.remove()
将会删除上一次next()
返回的元素。
特别注意:==仅仅只有next()能够移动迭代器,而remove()并不能移动迭代器!这意味着不能多次连续调用remove()来连续删除元素,而必须让remove()和next()间隔调用,否则会抛出异常!==
C++不仅能够通过加和减来移动迭代器,而且,remove()能够自动移动迭代器而不需要用户手动移动迭代器。
==对于映射:需要使用put()
和get()
来读取,而不是使用add()
和next()
==。
对于Map,使用get()访问而不是Collection()的next()。
是一个有序集合;元素的存储位置是有规律的;可以使用迭代器访问,也可以使用
get()
实现按索引的随机访问。
List使用的迭代器不是Iterator()而是ListIterator()。
==List也有数组和链表两种实现,都支持随机访问,但是链表实现的随机访问很慢。可以检测标记接口if( a_list instanceof RandomAccess)
来检测实现是否支持快速随机访问。==别忘了标记接口的作用:实际并不包含接口方法。只是象征地向用户或编译器说明一些类的特性。
- ArrayList
- LinkedList
- ArrayDeque
- HashSe–无序:并不是带有Set就一定有序。
- TreeSet
- EnumSet
- LinkedHashSet–记住元素插入顺序
- PriorityQueue
- HashMap
- TreeMap
- EnumMap
- LinkedHashMap
- WeakHashMap
- IdentityHashMap
反向遍历链表的时候,remove()删除刚刚遍历过的那个元素,而不是下一个元素。
==具有插入顺序即为有序,并不是必须满足字典序、大小序才是有序。==
==对于所有集合:当一个迭代器add/remove之后,另一个迭代器的next()将会抛出异常。一般允许有多个读迭代器,但只允许有一个写迭代器。==
==××注意:对链表的set(即修改元素的值而不增加、减少元素个数)并不会导致其他迭代器抛出异常××==。
==有两种访问元素的方法:1. 迭代器;2. get/set();第二种方法对于List来说开销比较大,但是有这种实现的API提供。
==注意常数的类型转换和变量的类型转换的区别:常数的类型转换使用后置转换:如将1.2转为float应该使用2.2F,而对于double才是使用(float)。==
注意:Java中所有的浮点常数都是double,除非加了后缀F,因此将浮点数赋值给float是会编译错误的。
2017年10月07日10:59:34
JAR即为Java归档。将工程中的各种文件和目录打包并压缩,最终提供的软件只需要一个文件即可。==压缩采用的格式为ZIP==。
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 指定程序入口点(指定程序的主类)
java -jar ApplicationName.jar
当然,要求windows包含JVM。