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修饰符。
- 局部内部类: 直接在方法中定义的类,java中特有的闭包实现形式
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. "); 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