Java与C++比较概况
C++ | Java |
---|---|
class Foo { // 声明 Foo 类 |
class Foo { // 定义类 Foo |
Foo a; |
Foo a; |
Foo b = a; |
Foo b = a.clone(); |
a.x = ; // 修改 a 对象 |
a.x = 5; // 修改 a 对象 |
cout << b.x << endl; |
System.out.println(b.x); |
Foo *c; |
Foo c; |
c = new Foo(); |
c = new Foo(); |
Foo *d = c; |
Foo d = c; |
c->x = ; |
c.x = 5; |
a.bar(); // 对 a 调用 Foo::bar() |
a.bar(5); // 对 a 调用 Foo.bar() |
cout << d->x << endl; |
System.out.println(d.x); |
-- 来自于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; |
char [2字节 16位Unicode] char ch1 = 'a'; |
Character chObj1 = new Character('a'); |
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; // 十进制 |
Integer.MIN_VALUE - Integer.MAX_VALUE Integer intObj = new Integer(56); |
long [8字节] long m = 356; |
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.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);
}
}
}