http://yishouce.com/java/run
http://www.shucunwang.com/RunCode/java/
数据类型
8种基本数据类型及对应的 类型封装器
byte, short, int, long -> Long,Integer,Short,Byte :Number
float, double -> Double,Float :Number
char, boolean -> Character,Boolean
java中其他数据类型都是由类封装,但基本数据类型不是,和c,c++一样,基本数据类型仅表示单个值,因为将基本数据类型设计为对象会极大地降低性能。
类型封装器:基本类型提供了性能方面的好处,但有时会需要对象的表现形式:传递引用及许多数据结构都是针对对象进行操作的。
数值类型封装器都继承自抽象类Number
Integer(int num)
Integer(String str) // NumberFormatException Integer iob = new Integer(100);
int i = iob.intValue(); // 从jdk 5开始,增加了两个重要特性,自动装箱和自动拆箱。
// 极大地简化了一些算法代码,也有助于防止出错,对泛型,集合框架非常重要。 Integer iob = 100;
int i = iob;
整型
- java的int总是32位,不随平台而变化。
- 没有无符号整数,增加了“无符号右移”运算符。
- 增加8位整型 byte,方便操作数据流及原始二进制数据。
public class test {
public static void main(String[] args) {
Integer a = -129;
Integer b = -129;
System.out.println(a == b); //false //Integer 直接缓存了 -128~127之间的数,原因是这些100内的数使用频度极高
//所以下例中 a == b是true,是因为它们确实指向了同一个实例
a = 1;
b = 1;
System.out.println(a == b); //true a = 128;
b = 128;
System.out.println(a == b); //false int m = 1;
float n = 2.1f;
m+=n; // m = (int)m+n;
System.out.println(m); // 3
//m = m + n; 报错,不能把float赋值给int
}
}float a=3;
double b=2; b+=a;
b=b+a;
a+=b;
a=a+b; //报错, a+b时会自动把a提升为double,而double类型不能直接赋值给float
a=(float)(a+b) // int类型的值 和 float类型的值相加,得到的结果是float而不是double
int a =1;
float b = 6;
b = b +a;16进制的字面量0x做前缀,8进制的字面量0做前缀
字符型char为16位
- java使用unicode表示字符,char为16位(一个unicode标量的宽度)
- 取值范围 0 ~ 65536
- java中 char 同c 一样 可以做整型使用
char ch1,ch2;
ch1 = 88;
ch2 = 'x';
System.out.println(ch1); // X
System.out.println(ch2); // x
System.out.println(++ch1); // Y
System.out.println(++ch2); // y
数组
java中只有动态数组,同时会严格检查数组下标,避免越界。
java中的数组是作为类的对象实现的。
// 申请内存与c++相同,但无需delete操作
int x[] = new int[12]; // 也可以直接初始化,java会自动创建足够大的数组,以容纳数组初始化器里的元素
int days[] = {31,28,31,30,31,30,31}; // 多维数组
int twoD[][] = new int[4][5]; // 手动分配第二维
int twoD2[][] = new int[4][];
twoD2[0] = new int[1];
twoD2[1] = new int[2];
twoD2[2] = new int[3];
twoD2[3] = new int[4]; // 初始化器
int twoD3[][] = {
{1,2},
{1,2,3},
{1,2,3,4}
}; // 另一种数组声明语法
int[] y = new int[12]; //int x[] = new int[12];
int[][] twoD4 = new int[4][5];//twoD[][] = new int[4][5];
在java中数组也是由类实现的,数组创建后如果没有初始化数据,则都会是默认值,即布尔类型默认为false,数值默认为0,其他默认为null。
public static void main(……){
int arr[] = {11 , 22 , 33};
Object nnn = arr;
chageArrA((int[])nnn);
System.out.println(arr[1]); // 44 if (nnn instanceof int[]){
System.out.println("OK"); // OK
}
} public static void chageArrA(int[] name){
name[1] = 44;
}
String
- 在java string不是关键字也不是保留字
- 每个字符串都是String的对象。
- 字符串对象是不可变的,修改字符串实际是创建了一个新的对象。
- 大量的字符串拼接操作,使用 StringBuffer和StringBuilder,它们是String的对等类,但它们允许修改字符串。
- 常用属性,方法: length, equals(), charAt()
- 命令行参数 main(String args[]){}
class CommandLine{
public static void main(String args[]){
for(int i=0; i< args.length;i++){
System.out.println("args["+i+"] : " + args[i]);
}
}
} // java CommandLine this is a test 100 - 1
public class test {
public static void main(String[] args) {
//java中的String类 在创建后就不可再被更改,所以其本身是线程安全的
//对于字符串直接量,JVM会使用一个字符串驻留池来缓存它们,一般情况下字符串驻留池里的字符串对象不会被GC回收,当再次使用驻留池中已有的字符串对象时,就无需再次创建了。
String aa = "32@#@";
String bb = "32@#@";
System.out.println(aa == bb);//true String cc = new String("32@#@");
System.out.println(aa == cc);//false //主动使用字符串驻留池,如果池中已有一个等于此字符串的对象(equals(Object)),
//则返回池中的字符串,否则,将此字符串添加到池中,并返回引用
cc = cc.intern();
System.out.println(aa == cc);//true
}
}
不支持指针
java 中不支持程序员操作指针,因为指针可能突破java执行环境和宿主计算机之间的防火墙,操作java运行时系统之外的地址。这与java的设计理念不合。
同时因为引用对象的变量的传递,实质是对指针的拷贝,所以java中没有真正的引用传递操作,因为JAVA认为没有指针也足够满足用户的需求。而c#中则可以使用ref。
public class test {
// change不是在改变传入对象的状态,而是改了str的指向
public static void change(String str){
str = "aaa";
}
public static void main(String[] args) {
// TODO Auto-generated method stub
String s = "1223";
change(s);
System.out.println(s);
}
}
// 结果: 1223,
// String 是类实现,属于引用传递,但在change方法中,str指向了一个新的实例,而不是修改传入的那个实例
运算符
>>>
按位右移 补0 (对某些非数值的数据进行移位操作时,并不希望出现符号扩展)
instanceof
判别对象是否是某类的实例
不短路的布尔逻辑运算
&,|
运算符分类
算数运算符:
+,-,*, / , %, ++, --, +=,-=,*=,/=,%=
位运算符:
~,&,|,^,>>, >>>, <<, &=, |= , ^= , >>= , >>>=, <<=
关系运算符:
==,!=,>,<,>=,<=
布尔逻辑运算符:
&,|,^, ||, &&,!,&=,|=,^=,==,!=,?:
赋值运算符:
=
没有运算符重载
equals与==
类的比较要用equals, 而不能用==,后者比较的是两个地址,即是恒等于
整型是会溢出的,所以有 i + 1< i 可能成立
同样 x>y||x<=y 也可能不成立,因为有NAN的存在
Double x = Double.NaN;
Float y = Float.NaN;
if(!(x>y||x<=y)){
System.out.println("ok");
}
类
Java类的初始化顺序 (静态变量、静态初始化块、变量、初始化块、构造器)
public class Singleton {
// 静态变量
public static String staticField = "静态变量";
// 变量
public String field = "变量"; // 静态初始化块
static {
System.out.println(staticField);
System.out.println("静态初始化块");
} // 初始化块
{
System.out.println(field);
System.out.println("初始化块");
} // 构造器
public Singleton() {
System.out.println("构造器");
}
}
// Java类的初始化顺序 (静态变量、静态初始化块、变量、初始化块、构造器)
- java中 未显式定义构造函数的类,编译器会自动添加默认构造器,将所有存储属性初始化为默认值。
- 类的方法名也可以与类同名,因为方法有返回值,而构造函数的返回值是缺省的。
- 创建类的实例,可以不用构造方法:
- 比如调用对象的clone()方法,从内存上对已有对象的影印。
- 运用反序列化手段,调用java.io.ObjectInputStream对象的 readObject()方法,是从文件中还原类的对象,也不会调用构造函数。
- java中没有析构函数,但提供了类似的finalize()方法。在回收类的对象时执行本方法。
- static, 静态变量的初始化如果需要计算,仅在第一次加载类时执行一次。
- 在类被加载时static修饰的成员字段被初始化,与类关联,只要类存在,static字段就存在。一个static字段单独划分一块存储空间,不与具体的对象绑定在一起,该存储空间被类的各个对象所共享。
- java中 静态变量和方法可以被实例对象调用(不推荐这样)
嵌套类, 内部类(非静态的嵌套类)
- 内部类中不能有静态成员
- 非静态方法,内部类里可以访问非静态字段,方法,也可以访问静态字段,方法,静态嵌套类
- 而静态方法,静态嵌套类里只能操作类中的静态元素,不能直接操作实例元素。因为非静态的方法,变量在实例化之前是不存在的
- 内部类实例化:MyClass.Nest2 nest = new MyClass().new Nest2();
- 同时静态类只能出现在内部,外部类不能使用static修饰符。
public class MyClass {
int x = 1;
static int y = 2;
void play(){
System.out.println(x);
}
static void play2(){
//静态方法中只能操作静态的变量和方法
//System.out.println(x);
y = 3;
System.out.println(y);
}
static class Nest1{
void play(int x){
//静态的嵌套类里也只能操作外部类的静态的变量和方法
//System.out.println(x);
System.out.println(x);
}
} class Nest2{
//Illegal static declaration in inner class MyClass.Nest2
//static String b = "";
void play(Object x){
System.out.println(x);
}
}
public static void main(String[] args) {
Nest1 a = new Nest1();
a.play(0); //non-static variable this cannot be referenced from a static context
Nest2 b = new MyClass().new Nest2();
b.play(0); new MyClass2().play();
}
} class MyClass2 {
MyClass.Nest2 n2;
MyClass.Nest1 n1; void play(){
// 静态嵌套类的实例化
n1 = new MyClass.Nest1();
n1.play(1); //内部类的实例化
MyClass outer = new MyClass();
n2 = outer.new Nest2();
n2.play(n2); MyClass.Nest2 n3 = outer.new Nest2();
n3.play(n3 );
MyClass.Nest2 n4 = new MyClass().new Nest2();
n4.play(n4);
}
} /*
0
0
1
MyClass$Nest2@659e0bfd
MyClass$Nest2@2a139a55
MyClass$Nest2@15db9742
*/
可变参数
使用数组传递未知数量参数,需要手动将这些参数打包为数组,不仅繁琐,而且容易出错。所以有了可变参数的诞生,将封装数组的操作交给了编译器。
void vaTest(int ... v){} // 与swift相比,省略号放在了前面。
可变参数的方法存在模糊性(Ambiguous)
class test {
void vaTest(int ... v){}
void vaTest(boolean ... v){}
void vaTest(int n, int ... v){} public static void main(String a[]){
vaTest(1,2,3);
vaTest(true);
vaTest(); // Error,Ambiguous
vaTest(1);// Error,Ambiguous
}
}
继承
super 可以访问父类中被子类重写的属性和方法 super , this
super.属性; //访问最近超类的属性
super.方法 // 访问最近超类的方法
super(); //访问最近超类的构造函数
构造函数调用机制
在类层次中,从超类到子类按照继承的顺序调用构造函数。
类A继承类B, 在实例化类A时,会先实例化类B,如果构造函数里有super(),则super()必须是子类构造函数中的第一条语句,而如果没有super(),那么也会先执行超类的默认构造函数或无参构造函数。
class A{
A(){
System.out.println("init A()");
}
} class B extends A{
B(){
System.out.println("init B()");
}
B(int x){
System.out.println("init B(int x)");
}
} class C extends B{
C(){
System.out.println("init C()");
} C(int x){
System.out.println("init C(int x)");
}
C(boolean x){
super(2);
System.out.println("init C(boolean x)");
}
} public class show2{
public static void main(String args[]){
C one = new C();
/**
* init A()
* init B()
* init C()
*/ C two = new C(1);
/**
* init A()
* init B()
* init C(int x)
*/ C three = new C(true);
/**
* init A()
* init B(int x)
* init C(boolean x)
*/
}
}
方法重写
java中可以直接重写超类的方法。
class A{
void callme(){
System.out.println("A's callme method");
}
} class B extends A{
void callme(){
System.out.println("B's callme method");
}
} class C extends B{
void callme(){
super.callme();
System.out.println("C's callme method");
}
} public class show2{
public static void main(String args[]){
A a = new A();
B b = new B();
C c = new C();
A r; r = a;
r.callme(); r = b;
r.callme(); r = c;
r.callme();
}
}
final
final 阻止变量修改
final 阻止方法重写
1. 将方法声明为final ,有时可以提高性能。编译器可以*地内联对这类方法的调用(当调用小的final方法时,编译器通常可以复制子例程的字节码,直接和调用方法的编译代码内联到一起。从而可以消除方法调用所需的开销。内联是final方法才有的选项。)。
2 . 通常java在运行时动态分析对方法的调用,这称为后期绑定。而final 方法的调用却可以在编译时解析,这称为早期绑定。
final 阻止继承
抽象类本身是不完整的,需要子类提供完整的实现,所以不能同时将类声明为abstract 和 final
Object类
java中所有其他类的超类,它的引用变量可以指向任何其他类的对象,包括数组。所有类的对象都可以使用Object类的方法。
Object clone(); 创建一个和将要复制的对象完全相同的新对象。
boolean equals(Object objects); 判断一个对象是否和另一个对象相等。
void finalize(); 在回首不再使用的对象之前使用
final Class<?>getClass() 在运行时获取对象所属的类
int hashCode() 返回与运行对象相关联的散列值
final void notify() 恢复执行在调用对象上等待的某个线程
final void notifyAll() 恢复执行在调用对象上等待的所有线程
String toString() 返回一个描述对象的字符串
final void wait() 等待另一个线程的执行
final void wait(long milliseconds)
final void wait(long milliseconds,int nanoseconds)
包 package
包是多个类的容器,作用类似于命名空间。
包是一种命名机制,也是一种可见性的控制机制,可以在包内定义包外部不能访问的类(不加访问修饰符)。
导入包:
import java.util.date;
import java.io.*;
访问控制public ,private, protected,及默认(不加访问修饰符)
package p1; public class Protection {
int n = 1;
private int n_pri = 2;
protected int n_pro = 3;
public int n_pub = 4; public Protection(){
System.out.println("base constructor");
System.out.println("n = " + n);
System.out.println("n_pri = " + n_pri);
System.out.println("n_pro = " + n_pro);
System.out.println("n_pub = " + n_pub);
}
} package p1; public class Derived extends Protection {
Derived(){
System.out.println("Derived constructor");
System.out.println("n = " + n);
// class only
//System.out.println("n_pri = " + n_pri);
System.out.println("n_pro = " + n_pro);
System.out.println("n_pub = " + n_pub);
}
} package p1; public class SamePackage {
SamePackage(){
Protection p = new Protection(); System.out.println("SamePackage constructor");
System.out.println("n = " + p.n);
// class only
//System.out.println("n_pri = " + p.n_pri);
System.out.println("n_pro = " + p.n_pro);
System.out.println("n_pub = " + p.n_pub);
}
} package p2; import p1.Protection; public class Protection2 extends Protection {
Protection2(){
System.out.println("Derived other package constructor");
// class or package only
//System.out.println("n = " + n);
// class only
//System.out.println("n_pri = " + n_pri);
System.out.println("n_pro = " + n_pro);
System.out.println("n_pub = " + n_pub);
}
} package p2; public class otherPackage {
otherPackage(){
p1.Protection p = new p1.Protection(); System.out.println("otherPackage constructor");
// class or package only
//System.out.println("n = " + p.n);
// class only
//System.out.println("n_pri = " + p.n_pri);
// class,subclass or package only
//System.out.println("n_pro = " + p.n_pro);
System.out.println("n_pub = " + p.n_pub);
}
}
接口 interface
接口和类的区别:接口不能有实例变量,无法维护状态信息。
- 可声明为public 或 默认访问级别,也可以用abstract
- java将接口从其实现中完全抽象出来,接口与抽象类很像,但接口还有其他功能:一个类可以实现多个接口,但一个类只能继承一个超类。
- 接口是对事物行为、属性的更高级别的抽象,放入接口中的全部都是不可变更的东西。同时接口是公开的,里面不能有私有的方法或变量。
- 接口中没有实例变量,接口内的方法没有方法体。
- 接口中可以声明变量,它们会被隐式地标识为 final 和 static。 实现接口的类不能修改它们。
- 接口中所有的变量和方法都被隐式地声明为 public。
- 如果在类实现的两个接口中都声明了同一个方法,则这两个接口的客户都将可以使用这个方法。
- 实现接口的方法必须被声明为 public。
- 如果类包含了一个接口,但并没有实现该接口定义的全部方法,那么必须将类声明为抽象类
interface CallBack{
int x = 0;
void callback(int param);
}
abstract class Incomplete implements CallBack {
int a,b;
void show {
System.out.println(a + " " + b);
}
// ...
}
接口扩展接口
interface A{
void method1();
} interface B extends A{
void method2();
}
接口中的变量
// 类似C 中的 #define 常量或 const 声明
// 如果接口不包含方法,那么实现该接口就相当于把接口中的常量导入到类名称空间中 import java.util.Random; interface ShareConstants{
int NO = 0;
int YES = 1;
int MAYBE = 2;
int LATER = 3;
int SOON = 4;
int NEVER = 5;
} class Question implements ShareConstants{
Random rand = new Random();
int ask(){
int prob = (int) (100 * rand.nextDouble());
if (prob < 30) {
// 在实例方法中直接使用静态变量,即可以 this.NO,而非必需Question.NO
return NO; // 30%
}
else if (prob < 60) {
return YES; // 30%
}
else if (prob < 75) {
return LATER; // 15%
}
else if (prob < 98) {
return SOON; // 13%
}
else {
return NEVER; // 2%
}
}
} class AskMe implements ShareConstants{
static void answer(int result){
switch(result){
case NO:
System.out.println("No");
break;
case YES:
System.out.println("Yes");
break;
case MAYBE:
System.out.println("Maybe");
break;
case LATER:
System.out.println("Later");
break;
case SOON:
System.out.println("Soon");
break;
case NEVER:
System.out.println("Never");
break;
}
} public static void main(String a[]){
Question q = new Question ();
answer(q.ask());
answer(q.ask());
answer(q.ask());
answer(q.ask());
answer(q.ask());
}
}
嵌套接口
将接口声明为某个类或另一个接口的成员。private,public, protected,或默认
// a nested interface example class A{
//a nested interface
public interface NestedIF{
boolean isNotNegative(int x);
}
} class B implements A.NestedIF{
public boolean isNotNegative(int x){
return x < 0 ? false : true;
}
} class NestedDemo{
public static void main(String args[]){
A.NestedIF nif = new B(); if (nif.isNotNegative(10))
System.out.println("ssss");
}
}
接口方法中添加默认实现(jdk 8 ),关键字default
在jdk 8之前,接口只能定义“有什么”,而不能定义“如何实现”。
从jdk 8开始,可以在接口方法中添加默认实现。然而,默认实现只是构成了一种特殊用途,接口最初的目的没有改变。
接口添加默认实现的好处有:
1. 提供了可选功能,接口方法 带有了默认实现,则实现该接口的类就不必在不需要该功能时提供占位符实现了。
2. 可以优雅地随时间演变接口,当为一个广泛使用的接口添加一个新方法时,不必破环现有代码。
// 接口默认方法的定义类似于为类定义方法,区别在于,需带关键字default public interface MyIF{
int getNumber(); default String getString(){
return "Default String";
}
}
默认实现的名称冲突
因为接口依然不能有实例变量。所以接口的特征没有改变,依然不能维护状态信息。
所以默认方法的引入,只是带来了额外的灵活性。并未有C++那样的多重继承。
但类确实可以从接口中继承一些行为,在一定程度上支持多重继承,也就可能发生名称冲突。
解决办法:
1. 在所有情况下,类实现的优先级高于接口的默认实现。类中的重写将覆盖接口中的默认方法,即便几个接口发生名称冲突,也会一起被重写。
2. 当类实现的两个接口提供了相同的默认方法,但是类没有重写该方法时,则会发生错误。
3. 如果一个接口继承了另一个接口,并且两个接口定义了相同的默认方法。那么继承接口的版本具有更高优先级。
接口可以定义一个或多个静态方法(jdk 8)
- 接口定义的静态方法可以独立于任何对象调用
- 实现接口的类或子接口不会继承接口中的静态方法。
public interface MyIF{
int getNumber();
default String getString(){
return "Default String ";
} static int getDefaultNumber(){
return 0;
}
} // 调用 int defNum = MyIF.getDefaultNumber();
动态方法调度(运行时多态的基础)
使用超类或接口,依赖倒置,有利于解偶,但要在运行时动态查找方法,性能上会增加额为负担。
所以在性能要求苛刻的代码中,应当谨慎小心。
异常处理
在不支持异常处理的语言中,必须手动检查和处理错误,而java的异常处理避免了这些问题,并且在处理过程中采用面向对象的方式管理运行时错误。
java异常是用来描述在一段代码中发生的异常情况的对象。当出现引起异常的情况时,就会创建用于表示异常的对象,并在引起错误的方法中抛出异常对象,方法可以选择自己处理异常,也可以继续传递异常,无论采用哪种方式,在某一点都会捕获并处理异常,异常可以由java运行时系统生成,也可以通过代码手动生成,前者与那些违反java语言规则或java执行环境的基础性错误有关,后者通常用于向方法的调用者报告某些错误条件。
5个关键字: try ,catch , throw ,finally ,和 throws
所有异常都是内置类 Throwable的子类。
紧跟Throwable之下的是两个子类,将异常分为两个不同的分支。
Exception:既可以用于用户程序应当捕获的异常情况,也可以用于创建自定义的异常类型的子类。
RuntimeException
Error:定义了在常规环境下不希望由程序捕获的异常,由java运行时系统使用,以指示运行时环境本身出现了某种错误。如堆栈溢出。通常是为了响应灾难性的失败而创建。应用程序通常不处理这类异常。
Throwable 重写了 toString()方法,从而可以返回一个包含异常描述的字符串。
未捕获的异常
如果程序没有捕获异常,当Java运行时系统检测到错误操作时,它会构造一个新的异常对象,然后抛出,这会导致程序终止执行。
因为异常一旦抛出,就必须要有一个异常处理程序捕获该异常,并立即进行处理。如果程序员没有提供任何自己的异常处理程序,该异常就会由Java运行时系统提供的默认处理程序捕获,默认处理程序会显示一个描述异常的字符串,输出异常发生点的堆栈踪迹并终止程序。堆栈轨迹会显示导致错误的方法的调用序列,可以精确定位导致错误发生的步骤序列,帮助程序员调试。
程序员自己处理异常 try ... catch
1. 可以修复错误,2.阻止程序自动终止。
大部分设计良好的catch子句,都应当能够分辨出异常情况,然后继续执行。
try-catch 语句由一个 try 块后跟一个或多个 catch 子句构成,这些子句指定不同的异常处理程序。引发异常时,运行时系统会查找处理此异常的 catch 语句。如果当前执行的方法不包含这样的 catch 块,则查看调用当前方法的方法,然后会遍历调用堆栈。如果找不到 catch 块,则 CLR 会向用户显示一条有关未处理异常的消息并停止执行程序。
使用多条catch语句时,异常子类必须位于所有超类之前。否则会编译错误 “Unreachable catch block”
public class test {
public static void main(String args[]){
try{
try{ // int a = args.length;
// System.out.println("a = " + a);
// int b = 42 / a;
// System.out.println(b);
//
int c[] ={1};
c[42] = 99; throw new NullPointerException("demo"); }
catch(ArithmeticException e){
System.out.println("Divide by 0:" + e);
}
catch(ArrayIndexOutOfBoundsException e){
System.out.println("Array index oob:"+ e);
}
finally{
System.out.println("finally");
}
System.out.println("after");
}
catch(Exception e){
System.out.println("Exception:"+ e);
}
}
}
throws(java的检查型异常处理)
如果方法可能引发自身不进行处理的异常,就必须指明这种行为,以便方法的调用者能够防备这些异常。 throws子句列出了方法可能抛出的异常类型。
除了Error和 RuntimeExceptin 及其子类类型,对于所有其他类型的异常都必需这样处理。
package throwsDemo; public class ThrowsDemo {
public static void main(String args[]){
try {
throwOne();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
static void throwOne() throws IllegalAccessException{
System.out.println("Inside throwOne");
throw new IllegalAccessException("demo");
}
}
在java.lang中,Java定义了一些异常类,这些异常类中最常用的是RuntimeException的子类。这些异常不需要包含在方法的throws列表中
异常 | 含义 |
ArithmeticException | 算术错误,例如除0 |
ArrayIndexOutOfBoundsException | 数组索引越界 |
ArrayStoreExcepion | 使用不兼容的类型为数组元素赋值 |
ClassCastException | 无效转换 |
EnumConstantNotPresentException | 使用未定义的枚举值 |
IllegalArgumentException | 使用非法参数调用方法 |
IllegalMonitorStateException | 非法的监视操作,例如等待未锁定的线程 |
IllegalThreadStateException | 环境或应用程序处于不正确的状态 |
IndexOutOfBoundsException | 某些类型的索引越界 |
NegativeArraySizeException | 使用负数长度创建数组 |
NullPointerException | 非法使用空引用 |
NumberFormatException | 字符串到数值格式的无效转换 |
SecurityException | 违反安全性 |
StringIndexOutOfBounds | 字符串索引越界 |
TypeNotPresentException | 类型未定义 |
UnsupportedOperationException | 不支持的操作 |
ClassNotFoundException | 类未定义 |
CloneNotSupportedException | 试图复制没有实现Cloneable接口的对象 |
IllegalAccessException | 对类的访问被拒绝 |
InstantiationException | 试图实例化一个抽象类或接口 |
InterruptedException | 一个线程被另一个线程中断 |
NotSuchFieldException | 请求的域变量不存在 |
NotSuchMethodException | 请求的方法不存在 |
ReflectiveOperationExcepton | 与反射相关的异常的超类 |
public class ThrowsDemo {
public static void main(String args[]){
try{
throwOne();
}
catch(Exception e){
e.printStackTrace();
}
}
static void throwOne(){
System.out.println("Inside throwOne");
throw new ClassCastException("demo");
}
}
自定义异常
class MyException extends Exception {
private int detail; MyException(int a){
detail = a;
} public String toString(){
return "MyException["+detail+"]";
}
} class ExceptionDemo{
static void compute(int a) throws MyException{
System.out.println("Called compute("+a+")");
if (a>10){
throw new MyException(a);
}
System.out.println("Normal exit");
} public static void main(String args[]){
try {
compute(1);
compute(20);
} catch (MyException e) {
// TODO Auto-generated catch block
System.out.println("Caught: "+e);
}
}
}
链式异常(jdk1.4)
通过链式异常,可以为异常关联另一个异常。
为了使用链式异常,Throwable 增加了两个构造函数和两个方法。
为异常设置原因:
Throwable(Throwable causeExc)
Throwable(String msg, Throwable causeExc)
Throwable initCause(Throwable causeExc)
查询引发异常的原因:
Throwable getCause() // 返回引发当前异常的异常,如果没有则返回null
public class ChainExcDemo {
static void demoproc(){ NullPointerException e = new NullPointerException("top layer"); e.initCause(new ArithmeticException("cause")); throw e;
} public static void main(String args[]){
try{
demoproc();
}catch(NullPointerException e){
System.out.println("Caught: " + e);
System.out.println("Original cause: " + e.getCause());
}
}
}
jdk 7 新增3个特性
带资源的try,当资源不再需要时会自动释放。
多重捕获,允许通过相同的catch子句捕获多个异常,每个多重捕获参数都被隐式声明为final
public class MultiCatch {
public static void main(String args[]){
int a = 10,b = 0;
int vals[] = {1,2,3}; try{
int result = a / b;
vals[10] =19; }
catch(ArithmeticException|ArrayIndexOutOfBoundsException e){
System.out.println("Exception Caught:" + e);
}
System.out.println("after");
}
}
更精确地重新抛出 more precise rethrow(最后重新抛出 final rethrow)
更精确地重新抛出 会对重新抛出的异常类型进行限制,只能重新抛出满足以下条件的经检查的异常:
由关联的try代码块抛出,没有被前面的catch子句处理过,并且是参数的子类型或者超类型。 为了强制使用这一特性,catch参数须被有效地或者显式地声明为final
class FirstException extends Exception{
FirstException(String s){
super(s);
}
} class SecondException extends Exception{
SecondException(String s){
super(s);
}
} public class Rethrowing {
// f会抛出一个异常
public static void f(boolean a) throws FirstException, SecondException {
System.out.println("originating the exception in f()");
if(a){
throw new FirstException("thrown from f()");
}
else{
throw new SecondException("thrown from f()");
}
} // g中捕获f抛出的Exception异常并重新抛出 // 报错,更精确的异常抛出 catch子句中抛出的异常必须来自try代码块,否则编译器无法解析
// 这里 throws子句需改为 throws Exception,恢复为普通的重新抛出异常
// public static void g() throws FirstException, SecondException {
// try {
// f(true);
// }
// catch(Exception e) {
// System.out.println("Inside g(), e.printStackTrace()");
//
// // 如果e 被重新赋值为其他异常
// e= new FirstException("22");
// throw e;
// }
// } // 更精确的重新抛出
// 即使 catch 子句的异常参数e的类型是 Exception,
// 但 throws 子句中却可指定异常类型为 FirstException 和 SecondException。
// Java SE 7编译器要求语句抛出的异常必须来自于 try 块,
// 并且 try 块抛出的异常只能是 FirstException 和 SecondException。
public static void g()throws FirstException, SecondException{
try {
f(true);
}
catch(final Exception e) {
System.out.println("Inside g(), e.printStackTrace()");
throw e;
}
} public static void main(String[] args){
try {
g();
}
catch(Exception e) {
System.out.println("Caught in main, e.printStackTrace()");
e.printStackTrace();
}
}
}
多线程
java内置支持多线程。多线程是特殊形式的多任务处理
多线程程序包含同时运行的多个部分,每个部分被称为一个线程,并且每个线程定义了一个单独的执行路径。
多任务处理 有两种类型: 基于进程和基于线程。
进程本身是程序,基于进程的多任务处理就是允许计算机同时运行多个程序的特性。程序时调度程序的最小代码单元。
而基于线程的多任务环境中,最小的可调度代码单元是线程。这意味着单个程序可以同时执行多个任务。
Thread类 和 Runnable接口
Thread作为线程代理,定义了一些管理线程的方法:
getName(),getPriority(), isAlive(), join() /*等待线程终止*/, run()/*线程的入口点*/, sleep()/*线程挂起*/, start()/*调用线程的run()方法启动线程*/
主线程: Thread.currentThread() (main/*默认名称*/,5/*优先级*/,main/*线程组*/)
在java程序启动时,会立即开始运行的一个线程。
其他子线程都是从主线程产生的。
通常主线程必须是最后结束执行的线程,因为它要执行各种关闭操作。
public class CurrentThreadDemo {
public static void main(String args[]){
Thread t = Thread.currentThread();
System.out.println("Current thread: "+ t); t.setName("my thread");
System.out.println("after name change: "+ t); try{
for(int n= 5; n>0;n--){
System.out.println(n);
Thread.sleep(1000);
}
}
catch(InterruptedException e){
System.out.println("InterruptedException:"+ e);
}
}
}
实现Runnable接口创建线程
只需实现run方法,就像main线程一样,区别在于run方法为程序中另外一个并发线程的执行建立了一个入口点,当run方法返回时,这个线程将结束。
class NewThread implements Runnable {
private Thread t;
private String name; NewThread(String threadName){
name = threadName;
t = new Thread(this,threadName);
System.out.println("Child thread:"+ t);
t.start();
} @Override
public void run() {
// TODO Auto-generated method stub
try{
for(int i =5; i>0;i--){
System.out.println("Child thread "+name+":"+ i);
Thread.sleep(500);
}
}
catch(InterruptedException e){
System.out.println("Child "+name+"interrupted:"+ e);
}
System.out.println("Exiting child "+name+"thread");
}
} class RunnableDemo {
public static void main(String args[]){
// 开启子线程
new NewThread("a");
new NewThread("b");
new NewThread("c"); try{
for(int i=5;i>0;i--){
System.out.println("Main thread:"+ i);
Thread.sleep(1000);
}
}
catch(InterruptedException e){
System.out.println("Main interrupted:"+ e);
}
System.out.println("Exiting Main thread");
} }
主线程必须在最后结束运行,因为对于某些旧的JVM,如果主线程在子线程完成之前结束,java运行时系统可能会挂起。
扩展Thread类 创建线程
class NewThread extends Thread{ NewThread(){
super("Demo Thread");
System.out.println("Child Thread:"+this);
start();
} public void run(){
try{
for(int i =5; i>0;i--){
System.out.println("Child thread:"+ i);
Thread.sleep(500);
}
}
catch(InterruptedException e){
System.out.println("Child interrupted:"+ e);
}
System.out.println("Exiting child thread");
}
} public class EntendThread {
public static void main(String args[]){
// 开启子线程
new NewThread(); try{
for(int i=5;i>0;i--){
System.out.println("Main thread:"+ i);
Thread.sleep(1000);
}
}
catch(InterruptedException e){
System.out.println("Main interrupted:"+ e);
}
System.out.println("Exiting Main thread");
}
}
join()
在主线程中执行子线程A.join(),则主线程会更这个子线程A执行完毕之后才会进行后续操作
class NewThread implements Runnable {
Thread t;
private String name; NewThread(String threadName){
name = threadName;
t = new Thread(this,threadName);
System.out.println("Child thread:"+ t);
t.start();
} @Override
public void run() {
// TODO Auto-generated method stub
try{
for(int i =5; i>0;i--){
System.out.println("Child thread "+name+":"+ i);
Thread.sleep(500);
}
}
catch(InterruptedException e){
System.out.println("Child "+name+"interrupted:"+ e);
}
System.out.println("Exiting child "+name+"thread");
}
} class RunnableDemo {
public static void main(String args[]){
// 开启子线程
NewThread A = new NewThread("a");
NewThread B = new NewThread("b");
NewThread C = new NewThread("c"); System.out.println("thread a is alive:"+ A.t.isAlive());
System.out.println("thread b is alive:"+ B.t.isAlive());
System.out.println("thread c is alive:"+ C.t.isAlive()); try{
System.out.println("waiting for threads to finish");
A.t.join();
B.t.join();
C.t.join(); for(int i=5;i>0;i--){
System.out.println("Main thread:"+ i);
Thread.sleep(1000);
}
}
catch(InterruptedException e){
System.out.println("Main interrupted:"+ e);
} System.out.println("thread a is alive:"+ A.t.isAlive());
System.out.println("thread b is alive:"+ B.t.isAlive());
System.out.println("thread c is alive:"+ C.t.isAlive()); System.out.println("Exiting Main thread");
} }
同步synchronized
synchronized方法
class Callme{
synchronized void call(String msg){
System.out.print("["+msg);
try{
Thread.sleep(1000);
}
catch(InterruptedException e){
System.out.println("InterruptedException");
}
System.out.println("]");
}
} class Caller implements Runnable{
String msg;
Callme target;
Thread t; public Caller(Callme targ,String s){
target= targ;
msg =s;
t = new Thread(this);
t.start();
} @Override
public void run() {
// TODO Auto-generated method stub
target.call(msg);
} } public class Synchronized {
public static void main(String args[]){
Callme target = new Callme();
Caller ob1 = new Caller(target,"hello");
Caller ob2 = new Caller(target,"Synchronized");
Caller ob3 = new Caller(target,"world"); try{
ob1.t.join();
ob2.t.join();
ob3.t.join(); }
catch(InterruptedException e){
System.out.println("InterruptedException");
}
}
}
synchronized 语句
class Callme{
void call(String msg){
System.out.print("["+msg);
try{
Thread.sleep(1000);
}
catch(InterruptedException e){
System.out.println("InterruptedException");
}
System.out.println("]");
}
} class Caller implements Runnable{
String msg;
Callme target;
Thread t; public Caller(Callme targ,String s){
target= targ;
msg =s;
t = new Thread(this);
t.start();
} @Override
public void run() {
// TODO Auto-generated method stub
synchronized(target){
target.call(msg);
}
}
} public class Synchronized {
public static void main(String args[]){
Callme target = new Callme();
Caller ob1 = new Caller(target,"hello");
Caller ob2 = new Caller(target,"Synchronized");
Caller ob3 = new Caller(target,"world"); try{
ob1.t.join();
ob2.t.join();
ob3.t.join(); }
catch(InterruptedException e){
System.out.println("InterruptedException");
}
}
}
进程间通信机制:wait(),notify()及notifyAll()
java通过wait(),notify()及notifyAll() 提供了一种进程间通信机制。这三个方法只能在同步上下文中调用。
wait() 方法通知调用线程放弃监视器并进入休眠。直到其他一些线程进入同一个监视器并调用notify()方法或notifyAll()方法。
notify()方法唤醒调用相同对象的wait()中的线程。
notifyAll() 方法唤醒调用相同对象的wait()中的所有线程,其中一个线程将得到访问授权。
wait()方法有另一种形式,可以指定等待的时间间隔。因为线程有极小可能被假唤醒(没有调用notify,而线程被恢复), 所以应当在一个检查线程等待条件的循环中调用wait()
// 队列
class Q{
int n;
boolean valueSet = false; synchronized int get(){
while(!valueSet){
try{
wait(); }
catch(InterruptedException e){
System.out.println("InterruptedException");
}
}
System.out.println("Got: "+ n);
valueSet =false;
notify();
return n;
} synchronized void put(int n){
while(valueSet){
try{
wait(); }
catch(InterruptedException e){
System.out.println("InterruptedException");
}
}
this.n =n ;
valueSet =true;
System.out.println("Put: "+ n);
notify();
}
} class Producter implements Runnable{
Q q; Producter(Q q){
this.q = q;
new Thread(this,"Producter").start();
} @Override
public void run() {
// TODO Auto-generated method stub
int i = 0;
while(i < 100){
q.put(i++);
}
}
} class Consumer implements Runnable{
Q q; Consumer(Q q){
this.q = q;
new Thread(this,"Consumer").start();
} @Override
public void run() {
// TODO Auto-generated method stub
while(q.get() < 99){
}
}
}
public class Wait2Notify {
public static void main(String args[]){
Q q = new Q();
new Producter(q);
new Consumer(q);
}
}
死锁
一个线程进入对象X的监视器,另一个线程进入对象Y的监视器,如果X中的线程试图调用对象Y的任何同步方法,那么都会被阻塞,需要等待当前占据对象Y的线程执行完毕,移出对象Y的监视器,而如果对象Y中的线程也试图调用对象X的任何同步方法,则会造成死锁。
class A{
synchronized void foo(B b){
String name = Thread.currentThread().getName();
System.out.println(name + " entered A.foo()"); try{
Thread.sleep(1000); }
catch(Exception e){
System.out.println("A Interrupted");
} System.out.println(name +" trying to call B.last()");
b.last();
} synchronized void last(){
System.out.println("Inside A.last()");
}
}
class B{
synchronized void bar(A a){
String name = Thread.currentThread().getName();
System.out.println(name + " entered B.bar()"); try{
Thread.sleep(1000); }
catch(Exception e){
System.out.println("B Interrupted");
} System.out.println(name +" trying to call A.last()");
a.last();
} synchronized void last(){
System.out.println("Inside B.last()");
}
}
public class DeadLock implements Runnable {
A a = new A();
B b = new B(); DeadLock(){
Thread.currentThread().setName("MainThread");
Thread t = new Thread(this,"RacingThread");
t.start(); a.foo(b);
System.out.println("back in main thread");
} public void run(){
b.bar(a);
System.out.println("back in other thread");
} public static void main(String args[]){
new DeadLock();
}
}
挂起,恢复,停止
java 2以前,程序使用Thread类定义的suspend(),resume(),stop()方法。这些方法在java2不再使用的原因是:
suspend()方法有时会导致严重的系统故障,假定线程为关键数据结构加锁,如果这是线程被挂起,那么这些锁将无法释放。其他可能等待这些资源的线程会被死锁。
resume()与suspend()是配对使用的,所以也一起废弃。
stop()方法有时也会造成严重的系统故障,假定线程正在向关键的重要数据结构中写入数据,并且只完成了部分发生变化的数据。如果这时停止线程,那么数据就够可能会处于损坏状态,问题是stop()会导致释放调用线程的所有锁,因此,另一个正在等待相同锁的线程可能会使用这些已损坏的数据。
所以现在面向函数编程开始流行,就是因为函数式编程不需要保存状态,也就不需要锁定,也就没有上述的各种状况。可以大大简化多线程操作的难度。
现在,线程被设计为run()方法周期性进行检查,以确保是否应当挂起,恢复或停止线程自身的执行。
class NewThread implements Runnable{
String name;
Thread t;
boolean suspendFlag; NewThread(String threadName){
name = threadName;
t = new Thread(this,name);
System.out.println("New Thread: "+ t);
suspendFlag = false;
t.start();
} @Override
public void run() {
// TODO Auto-generated method stub
try{
for(int i = 15; i>0;i--){
System.out.println(name +": "+ i);
Thread.sleep(2000);
synchronized(this){
while(suspendFlag){
wait();
}
}
}
}
catch(InterruptedException e){
System.out.println(name +" InterruptedException");
} System.out.println(name +" exiting");
} synchronized void mysuspend(){
suspendFlag =true;
}
synchronized void myresume(){
suspendFlag =false;
notify();
}
} public class Mysuspend {
public static void main(String args[]){
NewThread ob1 = new NewThread("one");
NewThread ob2 = new NewThread("two"); try{
Thread.sleep(1000);
ob1.mysuspend();
System.out.println("Suspending thread one");
Thread.sleep(1000);
ob1.myresume();
System.out.println("Resuming thread one"); ob2.mysuspend();
System.out.println("Suspending thread two");
Thread.sleep(1000);
ob2.myresume();
System.out.println("Resuming thread two");
}
catch(InterruptedException e){
System.out.println("Main thread InterruptedException");
} try{
System.out.println("Waiting for threads to finish");
ob1.t.join();
ob2.t.join();
}
catch(InterruptedException e){
System.out.println("Main thread InterruptedException");
} System.out.println("Main thread exiting");
}
}
线程状态: if(t.getState() == Thread.State.RUNNABLE)
BLOCKED/*因为正在等待需要的锁儿挂起执行*/,
NEW/*线程换没有开始执行*/,
RUNNABLE/*线程要么当前正在运行,要么在获取cpu的访问权后执行*/,
TERMINATED/*线程已经完成执行*/,
TIMED_WAITING/*线程挂起执行一段指定的时间,如sleep(),暂停版的wait(),join()*/,
WAITING/*线程因为某些动作而挂起执行,如调用非暂停版的wait(),join()*/
线程的上下文切换需要一定的开销,如果创建的线程太多,可能反而会降低程序的性能。 Fork/Join 框架创建可自动伸缩的计算密集型应用程序
Android中的Handler
/**
* 执行批量访问
*
* @param token access_token
*/
private void doRequestCurrent(final String token) {
for (UrlRequestBean bean : requestData) {
if (!bean.isCanel && !bean.isBegin) {
bean.isBegin = true;
doRequest(bean, token);
}
}
// 移除已取消的bean
Iterator<UrlRequestBean> iterator = requestData.iterator();
while (iterator.hasNext()) {
UrlRequestBean bean = iterator.next();
if (bean.isCanel) {
iterator.remove();
}
}
}
/**
* 访问接口,不会自动获取token
*
* @param urlBean UrlRequestBean
*/
private void doRequest(final UrlRequestBean urlBean, final String token) {
if (!urlBean.isCanel) {
urlBean.getBlocks().beforeRequest();
if (token != null) {
final Handler handler = new MyHandle(urlBean, false);
HttpPostThread httpPostThread = new HttpPostThread(handler, urlBean, token);
new Thread(httpPostThread).start();
}
}
} /**
* handler实现
*/
static private class MyHandle extends Handler {
private WeakReference<UrlRequestBean> urlBean_weak;
private boolean isTokenRequest = false; MyHandle(UrlRequestBean urlBean, boolean value) {
this.urlBean_weak = new WeakReference<>(urlBean);
isTokenRequest = value;
} public void handleMessage(Message msg) {
final UrlRequestBean urlBean = urlBean_weak.get();
if (urlBean.getContext() != null || isTokenRequest) {
switch (msg.what) {
case 200:
// 获取网络成功
Bundle data = msg.getData();
String s = data.getString("value");
// 返回信息输出
LogContent.printLog(s); if (!urlBean.isCanel && s != null) {
try {
JSONObject jsonObject = new JSONObject(s);
final Activity context = MyApplication.getTopActivity();
final DialogDiy2 dd = ((ActivityBase)context).dialogDiy2;
final CanGoToLoginActivity canGoToLoginActivity = new CanGoToLoginActivity();
final boolean goLogin = canGoToLoginActivity.isGoToLoginActivity(jsonObject);
if (goLogin) {
if (!dd.build.isShowing()) {
Log.d("test", String.valueOf(dd.build.isShowing()));
dd.showNormalDialog("您的账号在其它终端登录!", null, null, null, new DialogDiy2.MyDialogOnClickListener() {
@Override
public void onClick(View v) {
// 返回登陆页
urlBean.getBlocks().beforeGOlogin();
canGoToLoginActivity.goToLoginActivity(context);
}
});
}
} else {
// 解析数据
urlBean.getBlocks().onGetData(jsonObject);
}
} catch (Exception e) {
e.printStackTrace();
urlBean.getBlocks().onError();
}
}
break;
default:
LogContent.printLog(msg.what + ":handler.default");
if (!urlBean.isCanel) {
urlBean.getBlocks().onError();
}
break;
}
}
// 移除已完成的访问。
UrlRequestManager requestManager = UrlRequestManager.Instance;
requestManager.removeRequest(urlBean);
requestManager.isRefreshing = false;
}
} /**
* 异步线程
*/
private class HttpPostThread implements Runnable {
/**
* handler处理
*/
final private Handler handler;
final private UrlRequestBean urlBean;
final private String token; public HttpPostThread(Handler handler, UrlRequestBean urlBean, String token) {
this.handler = handler;
this.urlBean = urlBean;
this.token = token;
} @Override
public void run() {
// 获取我们回调主ui的message
String url = urlBean.getUrl();
try {
HttpPost httpRequest = new HttpPost(url); List<BasicNameValuePair> postValue = urlBean.getPostValue();
if (token != null) {
postValue.add(new BasicNameValuePair("access_token", token));
} httpRequest.setEntity(new UrlEncodedFormEntity(postValue, HTTP.UTF_8)); HttpClient client = SSLSocketFactoryEx.getNewHttpClient();
// 请求超时
client.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 10000);
// 读取超时
client.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT, 10000);
HttpResponse response = client.execute(httpRequest);
Log.d("test","begin");
int resultCode = response.getStatusLine().getStatusCode();
if (resultCode == 200) {
// 获得数据
String str = EntityUtils.toString(response.getEntity());
Message msg = new Message();
msg.what = resultCode;
Bundle data = new Bundle();
data.putString("value", str);
msg.setData(data);
handler.sendMessage(msg);
} else {
// 错误
Message msg = new Message();
msg.what = resultCode;
handler.sendMessage(msg);
}
} catch (Exception e) {
// 超时 or数据解析出错
e.printStackTrace();
Message msg = new Message();
msg.what = -1;
handler.sendMessage(msg);
}
}
}
枚举(jdk 5)
从jdk 5.0开始枚举 被添加到java语言中, 不同的是,java的枚举是用类实现的。所以可以具有构造函数,方法及实例变量,甚至实现接口。
enum Apple{
//枚举常量 被隐式声明为Apple的 public static final 成员,其类型声明为枚举的类型。
Jonathan,GoldenDel,RedDel,Winesap,Cortland
}
public class EnumDemo {
Apple ap = Apple.Cortland; // 比较相等性用 ==
boolean test(Apple a){
// 会显示名称 Jonathan
System.out.println(a); return a == ap;
} void switchTest(){
switch(ap){
case Jonathan:
// ...
case GoldenDel:
// ... default:
// ...
}
}
}
所有枚举自动包含两个预定义方法 values() 和 valueOf()
enum Apple{
//枚举常量 被隐式声明为Apple的 public static final 成员,其类型声明为枚举的类型。
Jonathan,GoldenDel,RedDel,Winesap,Cortland
}
public class EnumDemo {
public static void main(String args[]){
Apple ap; System.out.println("Here are all Apple constants"); Apple allApples[] = Apple.values();
for(Apple a : allApples){
System.out.println(a);
} ap = Apple.valueOf("GoldenDel");
System.out.println("ap contains " + ap);
}
}
// java枚举的类特性
enum Apple{
// 实例对象
Jonathan(10),GoldenDel(9),RedDel,Winesap(15),Cortland(8); // 变量
private int price; // 构造函数
Apple(int p){
price = p;
} Apple(){
price = -1;
}
// 方法
int getPrice(){
return price;
}
} class EnumDemo{
public static void main(String args[]){
Apple ap = Apple.Winesap;
System.out.println("Winesap costs "+ ap.getPrice() + " cents.\n"); System.out.println("All apple prices");
for (Apple a : Apple.values()){ if (a.compareTo(ap) < 0){
System.out.println(a.ordinal() +" : " +a+" costs "+ a.getPrice()+ " cents."+ a + " before "+ ap);
}
else if (a.compareTo(ap) > 0){
System.out.println(a.ordinal() +" : " +a+" costs "+ a.getPrice()+ " cents."+ a + " after "+ ap);
}
else {
System.out.println(a.ordinal() +" : " +a+" costs "+ a.getPrice()+ " cents."+ a + " equal "+ ap);
}
}
}
}
枚举不能继承其他类,因为所有枚举都自动继承超类 java.lang.Enum, 虽然同样具有面向对象的特性,但java的枚举与swift差别很大。
ordinal() 可以获取枚举常量在常量列表里的位置的值(序数值 0,1,2 ...)
compareTo 可以比较相同类型的两个枚举常量的序数值。
equals() 方法 等同 ==
测试
public class MyClass {
enum type{
A,B, MyClass3, C
}
enum type2{
A(3),B(4),C(7);
int index;
type2(int index){
this.index = index;
}
void print(){
System.out.println(index);
// 枚举中也不能调用外部类的实例变量
//System.out.println(x);
}
}
public static void main(String[] args) {
new MyClass2();
System.out.println(type.C.ordinal());
}
} public enum MyClass3{
A,C,B
} public class MyClass2 {
MyClass2(){
// 定义在外部的枚举,枚举值相当于几个静态常量
System.out.println(MyClass3.A);
System.out.println(MyClass3.C.ordinal());
// 枚举不能实例化
//MyClass3 D = new MyClass3(); // 定义在类内部的枚举
System.out.println(MyClass.type.C.ordinal());
System.out.println(MyClass.type2.C);
MyClass.type2.C.print();
// 枚举不能实例化
// MyClass.type2 D = new MyClass.type2(8);
}
} // 输出
// A
// 1
// 3
// C
// 7
// 3