可可西

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

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

 

Character chObj1 = new Character('a');
char ch2 = Character.toUpperCase('b');

byte [1字节]

byte a = 35;

 

Byte

Byte.MIN_VALUE - Byte.MAX_VALUE

Byte b1 = new Byte("23");

short [2字节]

short a = 870;

Short

Short.MIN_VALUE - Short.MAX_VALUE

Short s1 = new Short("315");

int [4字节]

int a = 56;  // 十进制
int b = 0210; // 八进制
int c = 0x2ff;  // 十六进制

Integer

Integer.MIN_VALUE - Integer.MAX_VALUE

Integer intObj = new Integer(56);
int intVal = intObj.intValue();

long [8字节]

long m = 356;
long n = 86L;

Long

Long.MIN_VALUE - Long.MAX_VALUE

Long s1 = new Long("-98166");

float [4字节]

float f = 3.5f;

Float

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

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会对容器内的元素进行排序;同理,相比HashMapTreeMap也会对容器内的元素进行排序
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);
        }
    }
}

 

posted on 2013-12-31 20:49  可可西  阅读(2056)  评论(0编辑  收藏  举报

导航