Java简明教程
Java与C++比较概况
C++ | Java |
---|---|
class Foo { // 声明 Foo 类 public: int x; // 成员变量 Foo(): x(0) { // Foo 的构造函数Constructor for Foo, } // 初始化 x int bar(int i) { // 成员函数 bar() return 3*i + x; } }; |
class Foo { // 定义类 Foo public int x = 0; // 成员变量, // 以及其值的初始化 public Foo() { // Foo的构造函数 } public int bar(int i) {// 成员方法 bar() return 3*i + x; } } |
Foo a; // 声明 a 为一个 Foo 类的对象值, // 使用其缺省的构造函数 // 如果你想要用其他的构造函数, // 你可以用 "Foo a(args);" |
Foo a; // 声明 a 为一个 Foo 类的对象的引用 a = new Foo(); // 使用缺省的构造函数初始化 // 如果你想要用其他的构造函数, // 你可以用 "Foo a = new Foo(args);" |
Foo b = a; // 拷贝 a 的内容到一个新的 Foo 类的变量 b 当中; // 另一种可以选择的语法是 "Foo b(a)" |
Foo b = a.clone(); // 拷贝所有a这个实例的成员到b,当且仅当, // Foo 实现了一个 public 的 clone() 方法, // 并且 clone() 返回一个新的这个对象的拷贝 |
a.x = 5; // 修改 a 对象 |
a.x = 5; // 修改 a 对象 |
cout << b.x << endl; // 输出 0,因为 b 和 a 是两个对象 |
System.out.println(b.x); // 输出 0,因为 b 和 a 是两个对象 |
Foo *c; // 声明 c 为指向一个 Foo 类对象的指针(初始值是 // 未定义的;可能指向任何地方) |
Foo c; // 声明 c 为一个指向 Foo 对象的指针 // (如果 c 是一个类的成员,那么初始值为空; // 如果 c 是一个局部变量那么你在使用之前必须 // 对它进行初始化) |
c = new Foo(); // 将 c 绑定为一个新的 Foo 对象的引用 |
c = new Foo(); // 将 c 绑定为一个新的 Foo 对象的引用 |
Foo *d = c; // 将 d 绑定为和 c 同一个对象的引用 |
Foo d = c; // 将 d 绑定为和 c 同一个对象的引用 |
c->x = 5; // 修改 c 指向的对象 |
c.x = 5; // 修改 c 指向的对象 |
a.bar(5); // 对 a 调用 Foo::bar() c->bar(5); // 对 *c 调用 Foo::bar() |
a.bar(5); // 对 a 调用 Foo.bar() c.bar(5); // 对 c 调用 Foo.bar() |
cout << d->x << endl; // 输出 5,因为 d 引用的对象和 c 一样 |
System.out.println(d.x); // 输出 5,因为 d 引用的对象和 c 一样 |
-- 来自于wiki - Comparison of Java and C++ en 中文
Java具有如下一些特性:
1. Java中基本数据为值类型,数组、枚举、类、接口均为引用类型。
2. Java中没有全局变量、全局函数、没有struct,没有union,所有东西必须写入类中。
3. Java用包代替了命名空间,用import关键字来导入外部包进行使用。
4. Java所有对象都从Object类单根继承,支持接口多继承。
5. Java没有预处理过程(不存在宏)、没有goto语句、没有指针、没有析构函数,不支持函数缺省参数,不支持运算符重载。
Java基本类型及其包装类
boolean [1字节] boolean flag = false; |
Boolean bObj1 = Boolean.TRUE; Boolean bObj2 = new Boolean(false); |
char [2字节 16位Unicode] char ch1 = 'a'; char ch2 = '\141'; // 8进制,'a' char ch3 = '\u0061'; // 16进制,'a' char ch4 = '\n'; char ch5 = '中'; |
Character chObj1 = new Character('a'); char ch2 = Character.toUpperCase('b'); |
byte [1字节] byte a = 35;
|
Byte.MIN_VALUE - Byte.MAX_VALUE Byte b1 = new Byte("23");
|
short [2字节] short a = 870;
|
Short.MIN_VALUE - Short.MAX_VALUE Short s1 = new Short("315");
|
int [4字节] int a = 56; // 十进制 int b = 0210; // 八进制 int c = 0x2ff; // 十六进制 |
Integer.MIN_VALUE - Integer.MAX_VALUE Integer intObj = new Integer(56); int intVal = intObj.intValue(); |
long [8字节] long m = 356; long n = 86L; |
Long.MIN_VALUE - Long.MAX_VALUE Long s1 = new Long("-98166");
|
float [4字节] float f = 3.5f;
|
Float.MIN_VALUE - Float.MAX_VALUE Float.NEGATIVE_INFINITY - Float. POSITIVE_INFINITY 0.0f/0.0f Float.NaN |
double [8字节] double d1 = 2.56; double d2 = 2.3e-2 |
Double.MIN_VALUE - Double.MAX_VALUE Double.NEGATIVE_INFINITY- Double. POSITIVE_INFINITY 0.0/0.0 Double.NaN |
基本类型的大小固定,与平台无关,因此Java没有像C++那样提供sizeof关键字来获取类型和对象的大小。
另外,Java不提供无符号的数据类型(unsigned)。
右移位运算符>>>
与逻辑右移位运算符功能类似,只是在左端末尾插入零值。>>则会在移位的同时插入符号位(即算术移位)
int n1 = -1; //n1=0xFFFFFFFF int n2 = n1>>1; //n2=0xFFFFFFFF int n3 = n1>>>1; //n3=0x7FFFFFFF
函数参数传递
Java函数参数没有指针传递、引用传递,只有值传递。
值类型参数会产生一个值类型副本,引用类型参数会产生一个引用类型副本(注意:引用类型间赋值不会产生新的对象,因此不会触发拷贝构造函数调用)
因此,想通过函数来实现两个数值变量或对象的交换是不行的,如下:
/***** AppMain.java *****/ public class AppMain { public static void fun(int n, Integer o) { n = 1; o = new Integer(1); } public static void swapInt(int a, int b) { int c = a; a = b; b = c; } public static void swapInteger(Integer oa, Integer ob) { Integer oc = oa; oa = ob; ob = oc; } public static void main(String[] args) { int n1 = 0; Integer on1 = new Integer(0); System.out.println("n1="+n1 + ", on1="+on1);//n1=0, on1=0 fun(n1, on1); System.out.println("n1="+n1 + ", on1="+on1);//n1=0, on1=0 int n2 = 1; Integer on2 = new Integer(1); System.out.println("n1="+n1 + ", n2="+n2); //n1=0, n2=1 System.out.println("on1="+on1 + ", on2="+on2);//on1=0, on2=1 swapInt(n1, n2); swapInteger(on1, on2); System.out.println("n1="+n1 + ", n2="+n2); //n1=0, n2=1 System.out.println("on1="+on1 + ", on2="+on2);//on1=0, on2=1 } }
其实,通过函数来实现两个数值变量或对象的交换也是有办法的(包裹:将要交换的数值变量或对象包裹到一个数组或类中)
/***** AppMain.java *****/ public class AppMain { public static void swapIntTrue(int[] a, int[] b) { int c = a[0]; a[0] = b[0]; b[0] = c; } public static void swapIntegerTrue(Integer[] oa, Integer[] ob) { Integer oc = oa[0]; oa[0] = ob[0]; ob[0] = oc; } public static void main(String[] args) { int n1 = 0; int n2 = 1; Integer on1 = new Integer(0); Integer on2 = new Integer(1); int nArray1[] = new int[1]; int nArray2[] = new int[1]; nArray1[0] = n1; nArray2[0] = n2; System.out.println("n1="+n1 + ", n2="+n2);//n1=0, n2=1 swapIntTrue(nArray1, nArray2); n1 = nArray1[0]; n2 = nArray2[0]; System.out.println("n1="+n1 + ", n2="+n2);//n1=1, n2=0 Integer onArray1[] = new Integer[1]; Integer onArray2[] = new Integer[1]; onArray1[0] = on1; onArray2[0] = on2; System.out.println("on1="+on1 + ", on2="+on2);//on1=0, on2=1 swapIntegerTrue(onArray1, onArray2); on1 = onArray1[0]; on2 = onArray2[0]; System.out.println("on1="+on1 + ", on2="+on2);//on1=1, on2=0 } }
自动封装(Autoboxing)&自动拆封(Autounboxing)
/***** AppMain.java *****/ public class AppMain { public static int autoBoxing(Integer o) { int sum = 2 + o;//自动拆封 return sum; } public static Integer autoUnBoxing(int n) { Integer o = new Integer(n); o += 2;//自动封装 return o; } public static void main(String[] args) { Integer oRet = autoBoxing(1);//参数与返回值均自动封装 int nRet = autoUnBoxing(oRet);//参数与返回值均自动拆封 } }
数组
// ----------------- 一维数组 ------------------ int nScores[] = {1,2,3,4,5,6,7,8,9,0}; String[] strContents = {"How", "Are", "You","?"}; float fDistances[] = new float[3]; // ----------------- 二维数组 ------------------ int nMatrix[][] = { {0, 1, 2}, {3, 4, 5}, {6, 7, 8} }; double[][] codes = new double[5][5]; long[] secs[] = new long[2][]; secs[0] = new long[3]; secs[1] = new long[4]; // ----------------- 数组长度 ------------------ int nWeek[] = {1, 2, 3, 4, 5, 6, 7}; int nLength = nWeek.length; // nLength = 7 // ----------------- 数组拷贝 ------------------ int nScores[] = {1,2,3,4,5,6,7,8,9,0}; int nScores2[] = new int[12]; System.arraycopy(nScores, 0, nScores2, 2, 8); // nScores2 = { 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 0,}
Arrays为数组的公用工具类,里面有大量静态成员函数供数组使用。
注释
// 单行注释 /* 块注释 */ /** doc注释(会被javadoc辨别)*/
package与import
package是Java提供的一种封装机制,可将一组类和接口封装在一个package中,缺省访问符修饰的类/接口、变量、方法在package内可见。package有效地管理类名空间,可防止类名冲突。
package语句作为Java源文件的第一条语句,指明该文件中定义的类/接口所在的包
package com.JavaApp; // 该源文件需要放在src/com/JavaApp目录中
如果源文件中没有package语句,则为无名包,该源文件存放在src/目录中
为了防止在使用类时带上长长的包路径,可使用import将包路径导入到当前源文件中
import java.applet.Applet; import java.awt.*;
for、break、continue增强
void breakTest() { int sum = 0; int[] code = {0,1,2,3,4,5}; // 针对数组、集合、枚举的for循环形式 JDK1.5之后增加 for (int e : code) { sum += e; if (sum >30) { break; } } L1:for(int i=0; i<100; i++) { L2:for(int j=0; j<100; j++) { if (i==50 && j==80) { // 跳出L1层循环 break L1; } } } } void continueTest() { int sum = 0; while(sum<1000) { if (++sum>100) { continue; } } C1:for(int i=0; i<100; i++) { C2:for(int j=0; j<100; j++) { if (i==50 && j==80) { // 继续C1层循环 continue C1; } } } }
字符串(16位unicode)
String 处理不变的字符串,任何对String的改变都会引发新的String对象的生成
String s1 = "hello"; // 静态创建字符串对象 String s2 = new String("你好"); // 动态创建字符串对象 int ns1Len = s1.length(); // ns1Len = 5 int ns2Len = s2.length(); // ns2Len = 2 String s3 = "hello"; String s4 = new String("你好"); String s5 = "Hello"; if (s1==s3) // true -s1与s3是否指向同一个对象 { System.out.println("yes!! s1==s3"); } if (s2==s4) // false -s2与s4是否指向同一个对象 { System.out.println("yes!! s2==s4"); } if (s1.equals(s5)) // false -s1与s5的字符串内容是否完全一致 { System.out.println("yes!! s1,s5 has same string value."); } if (s2.equals(s4)) // true -s2与s4的字符串内容是否完全一致 { System.out.println("yes!! s2,s4 has same string value."); } if (s1.equalsIgnoreCase(s5)) // true -忽略大小写,s1与s5的字符内容是否一致 { System.out.println("yes!! s1,s5 has same string value when ignore case."); } if (s2.equalsIgnoreCase(s4)) // true -忽略大小写,s2与s4的字符内容是否一致 { System.out.println("yes!! s2,s4 has same string value when ignore case."); }
String类的"="、"+"、"+=",看似运算符重载,实际只是java编译器做了一点手脚,对String的运算符做了特殊处理。
String s = "Hello "; s += "World!"; // 编译器转换成s = (new StringBuilder()).append(s).append("World!").toString();
StringBuffer 处理可变字符串(线程安全),不可被继承(final)
StringBuilder 处理可变字符串(线程不安全,拥有更高的性能),不可被继承(final),JDK1.5引入
StringBuffer与StringBuilder在使用上几乎没有区别
StringBuffer sb1 = new StringBuffer("Hello "); sb1.append("World!"); String str1 = sb1.toString(); // Hello World! StringBuilder sb2 = new StringBuilder("Hello "); sb2.append("World!"); String str2 = sb2.toString(); // Hello World!
成员权限控制
---------------------------- 访问修饰符 ---------------------------
# 当前类 同一package 子类 其他package
public √ √ √ √
protected √ √ √ ×
缺省 √ √ × ×
private √ × × ×
---------------------------------------------------------------------
类
访问修饰符 修饰符 class 类名称 extends 父类名称 implement 接口名称1, 接口名称2
1、每个类文件仅能有一个public class,可以存在多个其他的缺省class
2、public class的名称(包含大小写)必须和其类文件同名
3、一个类文件(*.java)中可以不存在public class
4、top class不能是private和protected(注:内部类可以)
修饰符:
final 当前类不能被继承
abstract 抽象类,不能被实例化;抽象类中不一定包含抽象方法,但包含了抽象方法的类一定要声明为抽象类
static 静态类;只有内部类才能定义为静态类
内部类
1、内部类拥有top class的所有特性
2、内部类可以是public、缺省、protected、private的
3、内部类可以访问其外部类的成员变量、方法,及所在外部类的其它内部类
静态类
1、只有内部类才能声明为static,也可以说是静态内部类
2、只有静态内部类才能拥有静态成员,普通内部类只能定义普通成员
3、静态类跟静态方法一样,只能访问其外部类的静态成员
4、如果在外部类的静态方法中访问内部类,这时候只能访问静态内部类
其他类中使用内部类
1、访问内部类,必须使用:外部类.内部类
2、静态内部类可以直接new
3、普通内部类必须绑定在其外部类的实例上
class A
{ static class Ainner1 {} class Ainner2 {} } class B { public test() { A.Ainner1 m1 = new A.Ainner1(); A m = new A(); A.Ainner2 m2 = m.new Ainner2(); } }
[静态] 成员变量
class A { // 调用顺序 【1】 public static int MAX_SIZE = 100; static final int WTN_TOTAL = 500; // static常量类型,定义时必须进行初始化 public int m_nVar = 9; final boolean m_switch = false; // 常量类型,定义时进行初始化 final int m_nVarConst; // final空白(只声明,不赋值),可在构造函数中为不同对象赋不同的值 protected String m_strName = "A"; private boolean m_bSex; float m_fDis = 8.0f; transient short m_shFlag = 10; // 告诉编译器,在类对象序列化的时候,此变量不需要持久保存 public static volatile boolean m_bRet; // 多线程时,要求编译器优化时保证对此变量的修改能够被正确的处理 // 调用顺序 【2】 // 类成员变量初始化器,可以有多个,按照先后顺序执行,但仅被初始化一次(当JVM加载类时执行) static { MAX_SIZE = 240; } static { } // 成员变量初始化器;运行于父类构造函数之后,自身构造函数之前 { m_nVar = 100; m_strName = "AA"; m_bSex = false; m_fDis = 12.0f; } // 调用顺序 【3】 public A() { MAX_SIZE = 200; m_nVar = 99; m_strName = "a"; m_bSex = true; m_fDis = 10.0f; // final空白,必须在构造函数中赋初值 m_nVarConst = 0; } public A(int nVar) { // final空白,必须在构造函数中赋初值 // 可在构造函数中为不同对象赋不同的值 // 一旦被赋值,就不可再改变 m_nVarConst = nVar; } // 参数nVar1不可在函数体中被修改 public void fun1(final int nVar1, int nVar2) { // nSum被赋值后不能被修改 final int nSum = nVar1 + nVar2; // static int nResult = 100; -- 非法,java不允许static局部变量 } }
[静态] 成员函数
class B { int m_nVar = 0; static short m_shVar = 10; // 无参构造函数 public B() { m_nVar = 1; } // 有参构造函数 public B(int nVar) { m_nVar = nVar; } // 拷贝构造函数 public B(B other) { this.m_nVar = other.m_nVar; } // finalize方法调用时机 // 1、显式的调用finalize方法 // 2、程序退出时为每个对象调用一次finalize方法 // 3、JVM按照某种策略(如内存不够)来进行垃圾回收时调用finalize;程序员可通过调用System.gc()来建议JVM进行垃圾回收 protected void finalize() { } // final类成员函数,不能被子类重写 // 在其函数体内,只能访问类成员变量和类成员函数,不能使用this、super等关键字 public final static void sFunc1() { m_shVar = 5; } // 类成员函数;在其函数体内,只能访问类成员变量和类成员函数,不能使用this、super等关键字 public static void sFunc2(int nVar) { m_shVar += nVar; } // final成员函数,可被子类继承,但不能被重写 public final void func1(int nVar) { m_shVar = (short)nVar; m_nVar = nVar; } // 成员函数 public void func2(int nVar) { m_shVar = (short)nVar; m_nVar = nVar; // 调用参数不定成员函数 fun3(); fun3(1); fun3(1,2); fun3(1,2,3); fun3(new int[]{1,2}); } // 参数不定成员函数 // 1、如果存在fun3()、fun3(int)等函数,在调用fun3时,优先匹配定长参数的函数 // 2、可变参数必须为函数参数列表中的最后一项 public void fun3(int... an) { for(int i=0;i<an.length;i++) { System.out.println(an[i]); } } // java native方法及JNI实例 // 用native定义的方法没有实现,而大多数情况下该方法的实现是用C、C++编写的 // JNI提供了运行时加载一个native方法的实现,并将其于一个Java类关联的功能 public native void displayHelloWorld(); /*1、synchronized关键字的作用域有二种: 1)对象实例范围。synchronized aMethod(){}可以防止多个线程同时访问这个对象的synchronized方法 (如果一个对象有多个synchronized方法,只要一个线程访问了其中的一个synchronized方法,其它线程不能同时访问这个对象中任何一个synchronized方法)。 然而,不同的对象实例的synchronized方法是不相干扰的。也就是说,其它线程照样可以同时访问相同类的另一个对象实例中的synchronized方法; 2)类的范围,synchronized static aStaticMethod{}防止多个线程同时访问这个类中的synchronized static方法 (如果一个类有多个synchronized static方法,只要一个线程访问了其中的一个synchronized static方法,其它线程不能同时访问这个类中任何一个synchronized static方法)。 它可以对类的所有对象实例起作用。 2、除了方法前用synchronized关键字,synchronized关键字还可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。 用法是: synchronized(B.class){区块},它的作用域是类B; synchronized(this){区块},它的作用域是当前this对象; synchronized(String obj){区块},它的作用域是当前String对象; 3、synchronized关键字不能被继承的,也就是说,基类的synchronized f()方法在继承类中并不自动是synchronized f(),而是变成了f()。 继承类需要显式的指定为synchronized方法。*/ // synchronized static 方法 public static synchronized void synFunc1() { } // synchronized方法 public synchronized void synFunc2() { } // synchronized块 public void synFun3(String strVar) { // 获得当前类锁。有线程访问时以下同步代码块时,其它线程的访问都被暂时阻塞 synchronized(B.class) { System.out.println("syn class!"); } System.out.println("Hello..."); // 获得当前strVar对象锁。有线程访问时以下同步代码块时,其它线程的访问都被暂时阻塞 synchronized(strVar) { System.out.println("syn var obj!"); } System.out.println("World..."); // 获得当前this对象锁。有线程访问时以下同步代码块时,其它线程的访问都被暂时阻塞 synchronized(this) { System.out.println("syn this!"); } } } // 抽象函数 abstract class C { // 不能将static方法、final方法或者类的构造器方法声明为abstract public abstract void aFunc(); public int func(int a, int b) { return a+b; } } // 抽象类不能实例化,被其他类继承后,必须实现抽象类中的抽象函数 class D extends C { public void aFunc() { } }
类继承
class CParent { String m_strName = "Parent"; int m_nParentAge; public CParent(int nAge) { m_nParentAge = nAge; } public void fun1() { } public void fun2() { } } class CChildren1 extends CParent { // CParent中的m_strName变量被隐藏 String m_strName = "Children"; int m_nChildrenAge; public CChildren1(int nAge) { // 由于父类没有缺省参数的构造函数,必须在构造函数中显示构造父类 super(nAge); } // 重写CParent中的fun1方法 // 1.重写的方法(子类)不能比被重写的方法(父类)有更严格的访问权限 // 2.重写的方法(子类)不能比被重写的方法(父类)产生更多的异常 public void fun1() { // this用来引用当前对象,super用来引用当前对象的父类 // 强制调用CParent的fun1方法 super.fun1(); // 强制访问CParent的m_strName String str1 = super.m_strName; // 访问自己的m_strName String str2 = this.m_strName; // 访问自己的m_strName时,可以不带上this String str3 = m_strName; } } class CChildren2 extends CParent { public CChildren2(int nAge) { super(nAge); } } class CGrandChildren1 extends CChildren1 { public CGrandChildren1(int nAge) { super(nAge); } } public class AppMain { // instanceof可以判定一个对象是否属于某个类的实例 // 在判定时应循序特殊到一般的原则,先判断子类,再判断父类 static int GetClassType(CParent obj) { if (obj instanceof CGrandChildren1) { return 11; } else if (obj instanceof CChildren1) { return 1; } else if (obj instanceof CChildren2) { return 2; } return 0; } public static void main(String[] args) { CParent c1 = new CChildren1(0); CParent c2 = new CGrandChildren1(0); int nClsType = GetClassType(c1); // 返回1 nClsType = GetClassType(c2); // 返回11 // 将c1强制转成CChildren1类型,这个过程叫造型 /* 如果c1为CChildren1类型时,代码不会有问题;若不是,系统会抛出ClassCastException的异常 因此,在造型之前使用instanceof进行判断是明智之举 */ CChildren1 cc = (CParent)c1; } }
接口
访问修饰符 interface 接口名称 extends 接口名称1, 接口名称2
访问修饰符:public 缺省
与类一样,public接口必须定义到自己独立的源文件中或类的内部,缺省接口可定义在任何位置
1、interface中数据成员均为公共类常量,具有public、static、final属性
2、interface中方法成员均为公共抽象方法,具有public、abstract属性
3、在接口继承关系中,如果子接口中定义了与父接口同名的常量和相同的方法,则父接口中的常量将被隐藏,方法被重写
interface E { int MAX_SIZE = 100; String NAME = "E"; void inter(); void interE(); } // 接口F必须写到F.java中 public interface F extends E { String NAME = "F"; // 接口E的NAME被隐藏 void inter(); // 接口E的inter()方法被重写 void interF(); } // 使用接口E,在new时重写接口E的方法 public static void main(String[] args) { E e = new E() { public void interE() { } }; }
枚举
枚举从JDK1.5才引进;与类一样,public枚举必须定义到自己独立的源文件中或类的内部,其他类型的枚举可定义在任何位置。
枚举具有类的绝大数特性,但也有一些特殊之处:
1、枚举常量具有public、static、final属性
2、枚举的构造函数必须为private
3、无法在外部或枚举内部new一个枚举对象
4、不支持类继承,支持接口多继承
public enum Week {Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday}; //////////////////////////////////////////////////////////////////////// enum Currency { Penny(1), Nickle(5), Dime(12), Quarter(100); private int m_nValue; private Currency(int nVal) { m_nValue = nVal; } int GetValue() { return m_nValue; } } //////////////////////////////////////////////////////////////////////// interface IBase1 { void fun1(); } interface IBase2 { void fun2(); } enum Planet implements IBase1, IBase2 { Mercury (3.303e+23, 2.4397e6), Venus (4.869e+24, 6.0518e6), Earth (5.976e+24, 6.37814e6), Mas (6.421e+23, 3.3972e6), Jupiter (1.9e+27, 7.1492e7), Saturn (5.688e+26, 6.0268e7), Uranus (8.686e+25, 2.5559e7), Neptune (1.024e+26, 2.4746e7); private final double m_mass; private final double m_radius; Planet(double mass, double radius) { m_mass = mass; m_radius = radius; } public void fun1() { } public void fun2() { } } //////////////////////////////////////////////////////////////////////// public static void main(String[] args) { Currency usCoin = Currency.Dime; switch (usCoin) { case Currency.Penny: System.out.println("Penny"); break; case Currency.Nickle: System.out.println("Nickle"); break; case Currency.Dime: System.out.println("Dime"); break; case Currency.Quarter: System.out.println("Quarter"); break; } if (Currency.Quarter == usCoin) { System.out.println("Quarter coin"); } for(Currency coin: Currency.values()) { System.out.println("coin: " + coin); } }
异常
所有的异常类必须从Throwable派生,异常分为两大类:一类是unchecked异常(橙色部分):
1、Error类异常由JVM生成并抛出(如:动态链接错误),我们不能在编程层面上解决Error,所以应该直接退出程序。
2、RuntimeException(及其衍生类)是Java程序自身造成的,RuntimeException完全可以通过修正Java程序避免。这类异常,Java编译器不强制要求程序员对其捕获和处理。
另外一类就是checked异常(淡蓝色部分):
Java编译器要求程序员必须捕获或声明所有的这类非运行时异常(如:文件找不到造成的IOException)。
/******************* 自定义异常 *******************/ class ExceptionSelf1Base extends Exception { private String m_strExpInfo; ExceptionSelf1Base(String strVal) { m_strExpInfo = strVal; } public String toString() { return "Exception: " + m_strExpInfo; } } class ExceptionSelf2Base extends Exception { private int m_nExpInfo; ExceptionSelf2Base(int nVal) { m_nExpInfo = nVal; } public String toString() { return "Exception: " + m_nExpInfo; } } class ExceptionSelf1 extends ExceptionSelf1Base { public ExceptionSelf1(String strVal) { super(strVal); } } /******************* 抛出异常 *******************/ class ExceptionTest { // 需要通过throws关键字声明函数内的所有抛出的checked异常 public void throwExp(int nMode) throws ExceptionSelf1,ExceptionSelf1Base,ExceptionSelf2Base { if (nMode < 0) { ExceptionSelf1Base e1 = new ExceptionSelf1Base("nMode < 0"); throw e1; } else if (nMode < 100) { ExceptionSelf2Base e2 = new ExceptionSelf2Base(100); throw e2; } ExceptionSelf1 e = new ExceptionSelf1("nMode >= 100"); throw e; } } /******************* 异常处理 *******************/ public class AppMain { public static void main(String[] args) { ExceptionTest expTest = new ExceptionTest(); try { expTest.throwExp(100); } // 捕捉异常时,需要按照特殊到一般的顺序进行捕捉 catch (ExceptionSelf1 e) { } catch (ExceptionSelf1Base e) { } catch (ExceptionSelf2Base e) { } catch (Exception e) { } // finally为非必需的块 // 进入try块后,无论发生还是不发生异常,finally块中的代码都要被执行,提供了统一的出口 finally { } } }
泛型
泛型从JDK1.5才引进;经常被称为参数化类型,它能够像方法一样接受不同类型的参数。
泛型中的通配符:
(1) 泛型中可以使用"?"通配符作为参数,表示该泛型可以接受任意类型的数据
(2) 上届通配符:只允许类A或类A的子类作为参数传入;表示方式:泛型类型<? extends A>
(3) 下届通配符:只允许类A或类A的父类作为参数传入;表示方式:泛型类型<? super A>
如果想从一个数据类型里获取数据,使用 ? extends A 通配符
如果想把对象写入一个数据结构里,使用 ? super A 通配符
如果既想存又想取,那就不要用通配符
/***** AppMain.java *****/ import java.util.*; public class AppMain { public static void main(String[] args) { //ArrayList<?> gList1 = new ArrayList<?>();//编译错误,通配符修饰的泛型不能用来直接创建对象 ArrayList<String> gStrList2 = new ArrayList<String>(); gStrList2.add("1"); ArrayList<?> gList2 = gStrList2; Object objVal = gList2.get(0); //通配符修饰的泛型可以读取数据到Object类型 //gList2.add("2"); //编译错误,【?】通配符修饰的泛型不能写入任何类型数据 //ArrayList<? extends Number> geList1 = new ArrayList<? extends Number>();//编译错误,通配符修饰的泛型不能用来直接创建对象 ArrayList<Integer> geIntList2 = new ArrayList<Integer>(); geIntList2.add(1); List<? extends Number> geList2 = geIntList2; Object objVa2 = geList2.get(0); //通配符修饰的泛型可以读取数据到Object类型 Number nVal = geList2.get(0); //【上届通配符】修饰的泛型可以读取数据到Number类型 //geList2.add(new Integer(2)); //编译错误,【上届通配符】修饰的泛型不能写入任何类型数据 //ArrayList<? super Integer> gsList1 = new ArrayList<? super Integer>();//编译错误,通配符修饰的泛型不能用来直接创建对象 ArrayList<Integer> gsIntList2 = new ArrayList<Integer>(); gsIntList2.add(1); List<? super Integer> gsList2 = gsIntList2; Object objVal3 = gsList2.get(0); //通配符修饰的泛型可以读取数据到Object类型 //Integer nVal2 = gsList2.get(0); //编译错误,【下届通配符】修饰的泛型不能读取数据到除Object类型中 gsList2.add(2); //【下届通配符】修饰的泛型可以写入Integer及其父类类型数据 /*** 自定义泛型 ***/ //Gencs<CString,String,String> gen1 = new Gencs<Float,String,String>();//编译错误,参数1必须为Number或其子类 Gencs<Integer,String,String> gen2 = new Gencs<Integer,String,String>(); Gencs<Float,String,String> gen3 = new Gencs<Float,String,String>(); gen3.setX(3.0f); Float fGen3 = gen3.getX(); Object oGen3 = gen3.getX(); //Integer iGen3 = gen3.getX();//编译错误,返回值为Float类型 //gen3.setX(3);//编译错误,必须传入Float类型参数 } } class Gencs<X extends Number, Y, Z> { private X m_x; //static Y m_y; //编译错误,不能用在静态变量中 public X getX() { return m_x; } public void setX(X x) { m_x = x; } public void fun() { //Z z = new Z();//编译错误,不能创建对象 } }
集合
注:点框的为接口、虚线框的为抽象类、实线框的为功能类(右下角的Collections为公用工具类,里面含有大量静态成员函数供集合使用)
集合只能容纳对象,不能容纳基本数据类型;元素通过实现Comparable接口、或提供一个实现Comparator接口的比较算法类来定义比较的规则。
List集合是有序集合(放入的顺序与存储的顺序一致),集合中的元素可以重复,访问集合中的元素可以根据元素的索引来访问。
Set集合是无序集合(放入的顺序与存储的顺序不一致),集合中的元素不可以重复,访问集合中的元素只能根据元素本身来访问。
Map集合中保存key-value对形式的元素,其key对象是不允许重复的(换个角度说:key对象的合集就是一个Set集合),访问时只能根据每项元素的key来访问其value;插入时如果key存在,则替换原来的value对象。
对象重复的含义:
(1) 两个对象e1和e2,如果e1.equals(e2)为true,则认为e1和e2重复,否则认为两个对象不重复
(2) 默认两个对象是否相等的equals方法是判断两个对象变量引用值是否指向同一个地址空间,我们可以重写equals方法来自定义重复的含义
ArrayList线程不安全;Vector线程安全(如果是单线程程序,推荐使用ArrayList)
ArrayList通过数组实现,LinkedList通过链表实现(不涉及到插入、删除等操作,推荐使用ArrayList)
与HashSet相比,TreeSet会对容器内的元素进行排序;同理,相比HashMap,TreeMap也会对容器内的元素进行排序
HashMap线程不安全,键与值都可以为null;Hashtable线程安全,键与值不能为null(如果是单线程程序,推荐使用HashMap)
/***** AppMain.java *****/ import java.util.*; public class AppMain { public static void main(String[] args) { /* ArrayList */ List list = new ArrayList(); list.add(new Integer(1)); list.add("2"); list.add(new Short((short)3)); ListIterator iList = list.listIterator(); while(iList.hasNext()) { System.out.println(iList.next()); } /* HashSet */ Set hSet = new HashSet(); hSet.add("1"); hSet.add(new Integer(2)); hSet.add("3"); Iterator iHSet = hSet.iterator(); while(iHSet.hasNext()) { System.out.println(iHSet.next()); } /* TreeSet */ Set tSet = new TreeSet(); tSet.add("1"); //tSet.add(new Integer(2)); //运行时错误,TreeSet会对元素进行排序,因此需要实现插入元素间相互比较的Comparable接口 tSet.add("3"); Iterator iTSet = tSet.iterator(); while(iTSet.hasNext()) { System.out.println(iTSet.next()); } /* HashMap */ HashMap hm = new HashMap(); hm.put(null, 0); hm.put("1", null); hm.put(new Integer(2), "2"); hm.put("3", new Float(3.0f)); Set sHm = hm.keySet(); Iterator iHm = sHm.iterator(); while(iHm.hasNext()) { Object k = iHm.next(); Object v = hm.get(k); System.out.println(k + "=" + v); } /* Hashtable */ Hashtable ht = new Hashtable(); // ht.put(null, 0); //运行时错误 // ht.put("1", null); //运行时错误 ht.put(new Integer(2), "2"); ht.put("3", new Float(3.0f)); Enumeration e = ht.keys(); while(e.hasMoreElements()) { Object k = e.nextElement(); Object v = ht.get(k); System.out.println(k + "=" + v); } /* TreeMap */ TreeMap tm = new TreeMap(); tm.put("1", new Long(1)); tm.put("2", new Double(2.0)); // tm.put(new Integer(3), "3"); //运行时错误,TreeMap会按照Key进行排序,因此Key需要实现插入元素间相互比较的Comparable接口 Set sTm = tm.keySet(); Iterator iTm = sTm.iterator(); while(iTm.hasNext()) { Object k = iTm.next(); Object v = tm.get(k); System.out.println(k + "=" + v); } } }
以上的示例为非泛型实现的集合,现在已不推荐使用了。(由于使用集合中的元素时,必须进行造型操作,效率低;而且造型操作可能在程序运行时出现问题)
泛型实现的集合在定义容器时,同时定义容器中对象的类型,这就使得容器内的元素只能是该对象类型或其子对象类型。
泛型实现的集合拥有与非泛型实现的集合同样的特性,一致的外部接口。(ArrayList LinkedList HashSet TreeSet HashMap TreeMap Hashtable)
/***** AppMain.java *****/ import java.util.*; public class AppMain { public static void main(String[] args) { /* ArrayList */ List<String> list = new ArrayList<String>(); list.add("1"); list.add("2"); list.add("3"); ListIterator<String> iList = list.listIterator(); while(iList.hasNext()) { System.out.println(iList.next()); } /* HashSet */ Set<Integer> hSet = new HashSet<Integer>(); hSet.add(1); hSet.add(new Integer(2)); hSet.add(new Integer(3)); Iterator<Integer> iHSet = hSet.iterator(); while(iHSet.hasNext()) { System.out.println(iHSet.next()); } /* TreeSet */ Set<String> tSet = new TreeSet<String>(); tSet.add("1"); //tSet.add(new Integer(2)); //编译时错误 tSet.add("3"); Iterator<String> iTSet = tSet.iterator(); while(iTSet.hasNext()) { System.out.println(iTSet.next()); } /* HashMap */ HashMap<String, Integer> hm = new HashMap<String, Integer>(); hm.put(null, 0); hm.put("1", null); hm.put("2", new Integer(2)); hm.put("3", new Integer(3)); Set<String> sHm = hm.keySet(); Iterator<String> iHm = sHm.iterator(); while(iHm.hasNext()) { String k = iHm.next(); Integer v = hm.get(k); System.out.println(k + "=" + v); } /* Hashtable */ Hashtable<String, Integer> ht = new Hashtable<String, Integer>(); //ht.put(null, 0); //运行时错误 //ht.put("1", null); //运行时错误 ht.put("2", new Integer(2)); ht.put("3", 3); Enumeration<String> e = ht.keys(); while(e.hasMoreElements()) { String k = e.nextElement(); Integer v = ht.get(k); System.out.println(k + "=" + v); } /* TreeMap */ TreeMap<String, Integer> tm = new TreeMap<String, Integer>(); tm.put("1", 1); tm.put("2", new Integer(2)); // tm.put(new Integer(3), 3); //编译时错误 Set<String> sTm = tm.keySet(); Iterator<String> iTm = sTm.iterator(); while(iTm.hasNext()) { String k = iTm.next(); Integer v = tm.get(k); System.out.println(k + "=" + v); } } }