Java 基础知识巩固(二)--持续更新
来源:互联网 发布:淘宝描述不符被下架 编辑:程序博客网 时间:2024/05/21 23:34
一 构造函数的相关注意
1 构造函数的修饰符
如果B是默认的访问权限,则构造方法相同。
2 构造函数的调用
1.子类的构造过程中必须调用父类的构造方法。 2.子类可在自己的构造方法中使用super()来调用父类的构造方法。 (1)使用this来调用本类的另外的构造方法。 (2)如果调用super必须写在子类构造方法的第一行。 3.如果子类的构造方法中没有显示的调用父类的构造方法,则系统默认的调用父类的无参的构造方法。 4.如果子类的构造方法中既没有显示调用父类的构造方法,而父类中又没有无参的构造方法,则编译出错。
3 构造函数不能被重写
这个问题,看起来非常简单,但是很多初学者学完继承后,都认为子类可以继承父类的一切,事实上,构造器Constructor不能被继承,因此不能重写Override,但可以被重载Overload。
希望大家一定注意,不是父类所有的东西都要继承.
二 overload和override的区别
1、方法名、参数、返回值相同。
2、子类方法不能缩小父类方法的访问权限。
3、子类方法不能抛出比父类方法更多的异常(但子类方法可以不抛出异常)。
4、存在于父类和子类之间。
5、方法被定义为final不能被重写。
overload(重载)
1、参数类型、个数、顺序至少有一个不相同。
2、不能重载只有返回值不同的方法名。(返回值不会引起重载)
3、存在于父类和子类、同类中。
二 ProcessBuilder 和 Runtime
ProcessBuilder.start() 和 Runtime.exec() 方法都被用来创建一个操作系统进程(执行命令行操作),并返回 Process 子类的一个实例,该实例可用来控制进程状态并获得相关信息。
Process 类提供了执行从进程输入、执行输出到进程、等待进程完成、检查进程的退出状态以及销毁(杀掉)进程的方法。创建进程的方法可能无法针对某些本机平台上的特定进程很好地工作,比如,本机窗口进程,守护进程,Microsoft Windows 上的 Win16/DOS 进程,或者 shell 脚本。创建的子进程没有自己的终端或控制台。它的所有标准 io(即 stdin、stdout 和 stderr)操作都将通过三个流 (getOutputStream()、getInputStream() 和 getErrorStream()) 重定向到父进程。父进程使用这些流来提供到子进程的输入和获得从子进程的输出。因为有些本机平台仅针对标准输入和输出流提供有限的缓冲区大小,如果读写子 进程的输出流或输入流迅速出现失败,则可能导致子进程阻塞,甚至产生死锁。 当没有 Process 对象的更多引用时,不是删掉子进程,而是继续异步执行子进程。 对于带有 Process 对象的 Java 进程,没有必要异步或并发执行由 Process 对象表示的进程。
每个ProcessBuilder实例管理一个进程属性集。ProcessBuilder的start()方法利用这些属性创建一个新的Process实例。start()方法可以从同一实例重复调用,以利用相同或者相关的属性创建新的子进程。
不同点:
ProcessBuilder.start() 和 Runtime.exec()传递的参数有所不同,Runtime.exec()可接受一个单独的字符串,这个字符串是通过空格来分隔可执行命令程序和参数的;也可以接受字符串数组参数。而ProcessBuilder的构造函数是一个字符串列表或者数组。列表中第一个参数是可执行命令程序,其他的是命令行执行是需要的参数。
通过查看JDK源码可知,Runtime.exec最终是通过调用ProcessBuilder来真正执行操作的。
- import java.io.BufferedReader;
- import java.io.File;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.InputStreamReader;
- import java.util.Map;
- public class ProcessBuilderTest {
- public static void restart() throws IOException {
- // Runtime 例子
- Process p;
- // test.bat中的命令是ipconfig/all
- String cmd = "c:\\test\\test.bat";
- try {
- // 执行命令
- p = Runtime.getRuntime().exec(cmd);
- // 取得命令结果的输出流
- InputStream fis = p.getInputStream();
- // 用一个读输出流类去读
- InputStreamReader isr = new InputStreamReader(fis);
- // 用缓冲器读行
- BufferedReader br = new BufferedReader(isr);
- String line = null;
- // 直到读完为止
- while ((line = br.readLine()) != null) {
- System.out.println(line);
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- // ProcessBuilder 例子 Java程序自重启
- // 用一条指定的命令去构造一个进程生成器
- ProcessBuilder pb = new ProcessBuilder("java", "-jar", "Test3.jar");
- // 让这个进程的工作区空间改为F:\dist
- // 这样的话,它就会去F:\dist目录下找Test.jar这个文件
- pb.directory(new File("F:\\dist"));
- // 得到进程生成器的环境 变量,这个变量我们可以改,
- // 改了以后也会反应到新起的进程里面去
- Map<String, String> map = pb.environment();
- Process p1 = pb.start();
- // 然后就可以对p做自己想做的事情了
- // 自己这个时候就可以退出了
- System.exit(0);
- }
- }
二 Timer TimerTask
一个TimerTask实际上就是一个拥有run方法的类,需要定时执行的代码放到run方法体内,TimerTask一般是以匿名类的方式创建。
java.util.Timer timer = new java.util.Timer(true); // true 说明这个timer以daemon方式运行(优先级低, // 程序结束timer也自动结束),注意,javax.swing // 包中也有一个Timer类,如果import中用到swing包, // 要注意名字的冲突。 TimerTask task = new TimerTask() { public void run() { ... //每次需要执行的代码放到这里面。 } }; //以下是几种调度task的方法: timer.schedule(task, time); // time为Date类型:在指定时间执行一次。 timer.schedule(task, firstTime, period); // firstTime为Date类型,period为long // 从firstTime时刻开始,每隔period毫秒执行一次。 timer.schedule(task, delay) // delay 为long类型:从现在起过delay毫秒执行一次 timer.schedule(task, delay, period) // delay为long,period为long:从现在起过delay毫秒以后,每隔period // 毫秒执行一次。
schedule(TimerTask task, long delay, long period)
schedule(TimerTask task, Date time, long period)
scheduleAtFixedRate(TimerTask task, long delay, long period)
scheduleAtFixedRate(TimerTask task, Date firstTime, long period)
当计划反复执行的任务时,如果你注重任务执行的平滑度,那么请使用schedule方法,如果你在乎的是任务的执行频度那么使用scheduleAtFixedRate方法。 例如,这里使用了schedule方法,这就意味着所有beep之间的时间间隔至少为1秒,也就是说,如果有一个beap因为某种原因迟到了(未按计划执行),那么余下的所有beep都要延时执行。如果我们想让这个程序正好在3秒以后终止,无论哪一个beep因为什么原因被延时,那么我们需要使用scheduleAtFixedRate方法,这样当第一个beep迟到时,那么后面的beep就会以最快的速度紧密执行(最大限度的压缩间隔时间)。
- 每一个Timer仅对应唯一一个线程。
- Timer不保证任务执行的十分精确。
- Timer类的线程安全的。
三 finally与return语句 完全理解
try/catch/finally语句下,finally子句是肯定会执行的。但是很多人做不同的测试,却得出了不同的结论。
具体的原理最好是去看《深入java虚拟机》,里面对jsr、ret等几个指令做了详细的说明。这里不深入分析,而仅仅是从表现形式上看一下finally的特征。
代码:
- /*
- * author: Zang XT
- */
- public class TestFinal {
- public static void main(String[] args) {
- System.out.println("test1:" + testFinal1());
- System.out.println("test2:" + testFinal2());
- System.out.println("test3:" + testFinal3());
- System.out.println("test4:" + testFinal4());
- }
- static int testFinal1() {
- int i = 1;
- try {
- return i;
- } finally {
- System.out.println("in testFinal1():finally 肯定会被执行的!");
- i = 48;
- }
- }
- static String testFinal2() {
- String str = "try";
- try {
- return str;
- } finally {
- System.out.println("in testFinal2():finally 肯定会被执行的!");
- str = "finally";
- }
- }
- static StringBuilder testFinal3() {
- StringBuilder build = new StringBuilder("try ");
- try {
- return build;
- } finally {
- System.out.println("in testFinal3():finally 肯定会被执行的!");
- build.append("finally");
- build = new StringBuilder("你猜我是谁!");
- }
- }
- static String testFinal4() {
- try {
- return "return in try";
- } finally {
- System.out.println("in testFinal4():finally 肯定会被执行的!");
- return "return in finally";
- }
- }
- }
输出是:
in testFinal1():finally 肯定会被执行的!
test1:1
in testFinal2():finally 肯定会被执行的!
test2:try
in testFinal3():finally 肯定会被执行的!
test3:try finally
in testFinal4():finally 肯定会被执行的!
test4:return in finally
关于 Java 虚拟机是如何编译 finally 语句块
finally在控制转移语句之前执行(控制 转移语句有:return-throw-continue-break)
四 java反射回顾
1.java反射机制的作用:
使java具备了动态性,可以在运行时构造任意一个类的对象,可以在运行时获取任意类的任意成员,并修改其成员变量或者调用其方法。写程序时(比如框架)无法知道将来要被调用的类名,所以,无法new对象。所以,反射机制解决了框架要解决的核心问题。反射就是把java类中的各个成分映射成相应的java类。比如:成员变量映射到Field类。2.反射机制的主要成员类。
Class类:位于java.lang包中,Class类的实例对象是某个类的字节码。以下类均位于java.lang.reflect包中:
Field 类:代表类的成员变量。该类提供一系列方法操作成员变量 Method类:代表类的方法。提供invoke方法,用以调用方法。
Constructor 类:Constructor 提供关于类的单个构造方法的信息以及对它的访问权限
Array类:提供了动态创建数组,以及访问数组的元素的静态方法。
Modifier 类提供了 static 方法和常量,对类和成员访问修饰符进行解码。
3.简单示例。
⑴.获取Class字节码的三种方式:⑵.获取类中成员,属性和方法:运行结果如图:⑶.反射获取构造函数,并构建对象:⑷.获取所需方法,并调用,以String类中concat()方法为例:⑸.数组的反射及应用。
具有相同类型和相同维数的数组,字节码相同。如:使用数组:
五 java注解回顾
注解,顾名思义,注解,就是对某一事物进行添加注释说明,会存放一些信息,这些信息可能对以后某个时段来说是很有用处的。 Java注解又叫java标注,java提供了一套机制,使得我们可以对方法、类、参数、包、域以及变量等添加标准(即附上某些信息)。且在以后某个时段通过反射将标注的信息提取出来以供使用。二、自定义Java标注
1 为什么要自定义注解
Java从1.5版本以后默认内置三个标注:
- @Override:只能用在方法之上的,用来告诉别人这一个方法是改写父类的。
- @Deprecated:建议别人不要使用旧的API的时候用的,编译的时候会用产生警告信息,可以设定在程序里的所有的元素上.
- @SuppressWarnings:这一个类型可以来暂时把一些警告信息消息关闭.
但是,仅仅这三个标注是不能满足我们开发时一些需求的。所以java允许我们自定义注解来使用。
2 如何自定义注解
自定义步骤大致分为两步:
- 通过@interface关键字(注意,不是interface,是@interace)声明注解名称,以及注解的成员属性或者叫做注解的参数。
- 使用java内置的四个元注解对这个自定义标注的功能和范围进行一些限制
问题来了,什么是元注解?
3 什么是元注解
元注解,就是定义注解的注解,也就是说这些元注解是的作用就是专门用来约束其它注解的注解。
请区别上面那三个注解,他们也是通过元注解定义而来的。
元注解有哪些呢,主要有四个@Target,@Retention,@Documented,@Inherited?
1 元注解有:@Target,@Retention,@Documented,@Inherited 2 3 @Target 表示该注解用于什么地方,可能的 ElemenetType 参数包括: 4 ElemenetType.CONSTRUCTOR 构造器声明 5 ElemenetType.FIELD 域声明(包括 enum 实例) 6 ElemenetType.LOCAL_VARIABLE 局部变量声明 7 ElemenetType.METHOD 方法声明 8 ElemenetType.PACKAGE 包声明 9 ElemenetType.PARAMETER 参数声明 10 ElemenetType.TYPE 类,接口(包括注解类型)或enum声明 11 12 @Retention 表示在什么级别保存该注解信息。可选的 RetentionPolicy 参数包括: 13 RetentionPolicy.SOURCE 注解将被编译器丢弃 14 RetentionPolicy.CLASS 注解在class文件中可用,但会被VM丢弃 15 RetentionPolicy.RUNTIME VM将在运行期也保留注释,因此可以通过反射机制读取注解的信息。 16 17 @Documented 将此注解包含在 javadoc 中 18 19 @Inherited 允许子类继承父类中的注解
4 自定义及使用注解示例
自定义一个类级别的标注Description
1 package lighter.javaeye.com; 2 import java.lang.annotation.Documented; 3 import java.lang.annotation.ElementType; 4 import java.lang.annotation.Retention; 5 import java.lang.annotation.RetentionPolicy; 6 import java.lang.annotation.Target; 7 8 @Target(ElementType.TYPE)//这个标注应用于类 9 @Retention(RetentionPolicy.RUNTIME)//标注会一直保留到运行时10 @Documented//将此注解包含在javadoc中11 public @interface Description {12 String value();13 }
再定义个方法级别的注解Name
1 package lighter.javaeye.com; 2 import java.lang.annotation.Documented; 3 import java.lang.annotation.ElementType; 4 import java.lang.annotation.Retention; 5 import java.lang.annotation.RetentionPolicy; 6 import java.lang.annotation.Target; 7 8 //注意这里的@Target与@Description里的不同,参数成员也不同 9 @Target(ElementType.METHOD)10 @Retention(RetentionPolicy.RUNTIME)11 @Documented12 public @interface Name {13 String originate();14 String community();15 }
然后使用以上两个注解
1 package lighter.javaeye.com; 2 3 @Description(value="javaeye,做最棒的软件开发交流社区") 4 public class JavaEyer { 5 @Name(originate="创始人:robbin",community="javaEye") 6 public String getName() 7 { 8 return null; 9 }10 11 @Name(originate="创始人:江南白衣",community="springside")12 public String getName2()13 {14 return "借用两位的id一用,写这一个例子,请见谅!";15 }16 }17 18 说明:其中标注“@Description(value="javaeye,做最棒的软件开发交流社区")”,可以写成“@Description("javaeye,做最棒的软件开发交流社区") ”,结果也是一样的。
因为Description标注定义的时候其参数(或者说属性)为value。
而value比较特殊,它在被指定参数的时候可以不用显示的写出来。
当然如果定义的时候参数名不是value而是其它的比如des,那么使用注解的时候,必须显示写出参数名,然后再赋值:@Description(Des=”xxx”)*/
提取出注解的信息
1 package lighter.javaeye.com; 2 3 import java.lang.reflect.Method; 4 import java.util.HashSet; 5 import java.util.Set; 6 7 public class TestAnnotation { 8 /** 9 * author lighter10 * 说明:具体关天Annotation的API的用法请参见javaDoc文档11 */12 public static void main(String[] args) throws Exception {13 String CLASS_NAME = "lighter.javaeye.com.JavaEyer";14 Class test = Class.forName(CLASS_NAME);15 Method[] method = test.getMethods();16 boolean flag = test.isAnnotationPresent(Description.class);17 if(flag)18 {19 Description des = (Description)test.getAnnotation(Description.class);20 System.out.println("描述:"+des.value());21 System.out.println("-----------------");22 }23 24 //把JavaEyer这一类有利用到@Name的全部方法保存到Set中去25 Set<Method> set = new HashSet<Method>();26 for(int i=0;i<method.length;i++)27 {28 boolean otherFlag = method[i].isAnnotationPresent(Name.class);29 if(otherFlag) set.add(method[i]);30 }31 for(Method m: set)32 {33 Name name = m.getAnnotation(Name.class);34 System.out.println(name.originate());35 System.out.println("创建的社区:"+name.community());36 }37 }38 }
六 java字符集和编码
1.为什么要编码
计算机本质上只能处理数值,每个字符在计算机中只能用一个整数来表示,这种整数称为字符编码。2.编码格式
ASCII码总共有 128 个,用一个字节的低 7 位表示,0~31 是控制字符如换行回车删除等;32~126 是打印字符,可以通过键盘输入并且能够显示出来。
ISO-8859-1
ISO 组织在 ASCII 码基础上又制定了一些列标准用来扩展 ASCII 编码,其中 ISO-8859-1 涵盖了大多数西欧语言字符,
所有应用的最广泛。ISO-8859-1 仍然是单字节编码,它总共能表示 256 个字符。
GB2312
它的全称是《信息交换用汉字编码字符集 基本集》,只收录简体汉字,共有7445个字符。
机内码使得汉字编码的空间为0xA1A1到0xF7FE,每个汉字的编码为两个字节,每个字节的编码在0xA1到0xFE之间
ASCII的编码是一个字符一个字节,每个字节的编码在0x00到0x7F之间,这样就使得英文和汉字的编码空间不再重叠.
注:区号加位号可以作为一种编码,这种编码称为区位码.区位码不能直接在机器中使用,因为它的编码空间与ASCII重叠,
为了解决这个问题GB2312的字符在机器中实际采用的是机内码,通过一个简单的对应关系来实现两者之间的转换。
GBK
全称叫《汉字内码扩展规范》,GBK包含了GB2312的全部字符,同时还包括了Big5中的全部字符以及日文、韩文中使用的汉字。
对于原GB2312中的字符,GBK的编码就是原来的机内码。
GBK的编码空间从0x8140到0xFEFE,每个汉字的编码仍为两字节,其中第一个字节8位二进制数的最高位始终是1,
这使得GBK的编码空间可以避开ASCII的编码空间。
注:<问题>:虽然GB2312和GBK都有效的避开了和ASCII编码空间的重叠,但是并没有和其他一些编码(比如Big5)避开重叠,
这样的话有可能我的一段文字到别人那边就变成另外一段文字,并不是很明显的出现乱码。
解决这种问题的最好办法是统一编码,不管简体字还是繁体字,还有日文、韩文中使用的汉字,全部统一编码,
使得每个编码对应的汉字是唯一的,并且与世界上所有其它语言的字符编码不重叠,这就是Unicode。
Unicode编码:
编码方法是首先将所有的字符排列在若干个平面上,每个平面划分为256行,每行256列,从而每个平面存放64K(1K=1024)个字符。
每个字符所在的平面号、行号和列号称为码位(code point),码位可作为字符的编码。
平面0是最重要的平面,称为BMP(Basic Multilingual Plane),其中基本上包括了当今世界上各种语言使用的字符,
当然也包含中、日、韩三种文字中使用的所有汉字。
理论上ISO/IEC 10646预留了64K个平面,这样平面号的编码范围将为0x0000~0xFFFF,加上平面内的行号和列号,
字符码位的范围将是0x00000000~0xFFFFFFFF,也就是说,必须用4个字节来存放一个字符的码位。 Unicode仅使用编号0x00到0x10的17个平面,
其码位范围为0x000000~0x10FFFF,也需要用3个字节来存放。 但是我们实际使用的字符绝大多数都在BMP中,
只是偶尔使用其它平面的字符,因此可以设计一种两字节的编码,使得BMP中的字符编码就是它们在BMP中的码位,
而其它平面的字符用一种代替的方法来表示,这样的编码就是UTF-16。
UTF-16
UTF-16 具体定义了 Unicode 字符在计算机中存取方法。UTF-16 用两个字节来表示 Unicode 转化格式,这个是定长的表示方法,
不论什么字符都可以用两个字节表示,两个字节是 16 个 bit,所以叫 UTF-16。
UTF-16 表示字符非常方便,每两个字节表示一个字符,这个在字符串操作时就大大简化了操作,
这也是 Java 以 UTF-16 作为内存的字符存储格式的一个很重要的原因。
UTF-8
UTF-8采取不定长的编码方法:
ASCII字符的编码只用一个字节,并且其编码就是原来的ASCII码, 其它字符的编码从2个字节到4个字节不等。
每个字符编码的第一字节指出该字符编码的字节数:其中第一个二进制数0的左边有多少个1,该字符编码就有多少个字节;
如果第一位就是0,则该字符编码只有一个字节。
例如,某个字符编码第一字节是0xxxxxxx(x表示任意的二进制数),则该字符的UTF-8编码只有一个字节。
若某个字符编码第一字节为110xxxxx,则该字符的编码将有两个字节。对于两个字节及其以上的UFT-8编码,
从第二字节开始,每个字节开头两个二进制数都是10。
java中常用到编码的地方
用一句话描述一下编码:任何我们输入的字符当需要被计算机保存或者用于传输时都需要进行编码。
问题在于需不需要你去显示的编码<不知道准不准确>
1.内存中操作的编码
情况1:Java 中用 String 表示字符串,所以 String 类就提供转换到字节的方法,也支持将字节转换为字符串的构造函数。
1
String s =
"中国"
;
2
byte
[] b = s.getBytes(
"UTF-8"
);
3
String n =
new
String(b,
"UTF-8"
);
情况2:Charset 提供 encode 与 decode 分别对应 char[] 到 byte[] 的编码和 byte[] 到 char[] 的解码
1
Charset charset = Charset.forName(
"UTF-8"
);
2
ByteBuffer byteBuffer = charset.encode(string);
3
CharBuffer charBuffer = charset.decode(byteBuffer);
2.I/O 操作中存在的编码:I/O 包括磁盘 I/O 和网络 I/O (在转换流上注意编码的设置)
Reader 类是 Java 的 I/O 中读字符的父类,而 InputStream 类是读字节的父类,InputStreamReader 类就是关联字节到字符的桥梁, 它负责在 I/O 过程中处理读取字节到字符的转换,而具体字节到字符的解码实现它由 StreamDecoder 去实现,在 StreamDecoder 解码过程中必须由用户指定 Charset 编码格式。值得注意的是如果你没有指定 Charset,将使用本地环境中的默认字符集
写的情况也是类似,字符的父类是 Writer,字节的父类是 OutputStream,通过 OutputStreamWriter 转换字符到字节。
01
// 写字符换转成字节流
02
FileOutputStream outputStream =
new
FileOutputStream(
"c:/stream.txt"
);
03
OutputStreamWriter writer = new OutputStreamWriter(05
try
{
06
writer.write(
"中国"
);
07
}
finally
{
08
writer.close();
09
}
10
// 读取字节转换成字符
11
FileInputStream inputStream =
new
FileInputStream(file);
12
InputStreamReader reader =
new
InputStreamReader(
13
inputStream, charset);
14
StringBuffer buffer =
new
StringBuffer();
15
char
[] buf =
new
char
[
64
];
16
int
count =
0
;
17
try
{
18
while
((count = reader.read(buf)) != -
1
) {
19
buffer.append(buffer,
0
, count);
20
}
21
}
finally
{
22
reader.close();
23
}
七 枚举关键知识
(1)使用enum关键字定义枚举类。枚举类一样可以有自己的Field、方法,可以实现一个或多个接口,也可以有自己的构造器。
(2)使用eunm定义的枚举类直接继承了java.lang.Enum类,间接继承Object类。
(3)使用enum定义、非抽象的枚举类默认会使用final修饰,因此枚举类不能派送子类。(并不是所有的枚举类都使用final修饰,如抽象枚举类)
(4)枚举类所有实例必须在枚举类的第一行显示列出,否则这个枚举类永远不能产生实例。
(5)所有枚举类都提供一个values方法,该方法可以方便地遍历所有枚举值。
1 Java 枚举7常见种用法
DK1.5引入了新的类型——枚举。在 Java 中它虽然算个“小”功能,却给我的开发带来了“大”方便。
用法一:常量
在JDK1.5 之前,我们定义常量都是: public static fianl.... 。现在好了,有了枚举,可以把相关的常量分组到一个枚举类型里,而且枚举提供了比常量更多的方法。
- public enum Color {
- RED, GREEN, BLANK, YELLOW
- }
用法二:switch
JDK1.6之前的switch语句只支持int,char,enum类型,使用枚举,能让我们的代码可读性更强。
- enum Signal {
- GREEN, YELLOW, RED
- }
- public class TrafficLight {
- Signal color = Signal.RED;
- public void change() {
- switch (color) {
- case RED:
- color = Signal.GREEN;
- break;
- case YELLOW:
- color = Signal.RED;
- break;
- case GREEN:
- color = Signal.YELLOW;
- break;
- }
- }
- }
用法三:向枚举中添加新方法
如果打算自定义自己的方法,那么必须在enum实例序列的最后添加一个分号。而且 Java 要求必须先定义 enum实例。
- public enum Color {
- RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);
- // 成员变量
- private String name;
- private int index;
- // 构造方法
- private Color(String name, int index) {
- this.name = name;
- this.index = index;
- }
- // 普通方法
- public static String getName(int index) {
- for (Color c : Color.values()) {
- if (c.getIndex() == index) {
- return c.name;
- }
- }
- return null;
- }
- // get set 方法
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public int getIndex() {
- return index;
- }
- public void setIndex(int index) {
- this.index = index;
- }
- }
用法四:覆盖枚举的方法
下面给出一个toString()方法覆盖的例子。
- public enum Color {
- RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);
- // 成员变量
- private String name;
- private int index;
- // 构造方法
- private Color(String name, int index) {
- this.name = name;
- this.index = index;
- }
- //覆盖方法
- @Override
- public String toString() {
- return this.index+"_"+this.name;
- }
- }
用法五:实现接口
所有的枚举都继承自java.lang.Enum类。由于Java 不支持多继承,所以枚举对象不能再继承其他类。
- public interface Behaviour {
- void print();
- String getInfo();
- }
- public enum Color implements Behaviour{
- RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);
- // 成员变量
- private String name;
- private int index;
- // 构造方法
- private Color(String name, int index) {
- this.name = name;
- this.index = index;
- }
- //接口方法
- @Override
- public String getInfo() {
- return this.name;
- }
- //接口方法
- @Override
- public void print() {
- System.out.println(this.index+":"+this.name);
- }
- }
用法六:使用接口组织枚举
- public interface Food {
- enum Coffee implements Food{
- BLACK_COFFEE,DECAF_COFFEE,LATTE,CAPPUCCINO
- }
- enum Dessert implements Food{
- FRUIT, CAKE, GELATO
- }
- }
用法七:关于枚举集合的使用
java.util.EnumSet和java.util.EnumMap是两个枚举集合。EnumSet保证集合中的元素不重复;EnumMap中的key是enum类型,而value则可以是任意类型。关于这个两个集合的使用就不在这里赘述,可以参考JDK文档。
关于枚举的实现细节和原理请参考:
二 枚举类型的使用介绍和静态导入
JDK5.0中加入了枚举类型,使用enum关键字定义,可以按照如下定义:
public enum Weather{ Sunny, Rainy, Cloudy,}
values() 获取枚举类型的所有枚举常量
valueOf(Class<T> enumType, String name)返回带指定名称的指定枚举类型的枚举常量。
for(Weather weather : Weather.values()){ System.out.println(weather);}System.out.println(Weather.valueOf(Weather.class, "Sunny"));
for(Weather weather : Weather.values()){ System.out.println(weather);}//以下输出为SunnySystem.out.println(Weather.valueOf(Weather.class, "Sunny"));
public enum Weather{ Sunny("晴天"), Rainy("雨天"), Cloudy("多云"); //私有成员变量,保存名称 private String value; public String getValue() { return value; } //带参构造函数 Weather(String value){ this.value = value; }}public static void main(String[] args) { Weather weather1 = Weather.Sunny; //以下输出为"晴天" System.out.println(weather1.getValue());}
enum关键字的作用类似于class或interface,本质上是在定义一个类别,细节的实现由编译器完成。
自定义的枚举类型实质上继承自java.lang.Enum抽象类。而每一个成员常量其实就是自己定义的枚举类型的一个实例,都被定义为final,所有无法改变他们,另外他们是static,pulibc的,即:
public static final 枚举常量;
在运行期间我们无法再使用该枚举类型创建新的实例,这些实例是在编译期间就完全确定下来了的。
compareTopublic final int compareTo(E o)比较此枚举与指定对象的顺序。在该对象小于、等于或大于指定对象时,分别返回负整数、零或正整数。 枚举常量只能与相同枚举类型的其他枚举常量进行比较。该方法实现的自然顺序就是声明常量的顺序。指定者:接口 Comparable<E extends Enum<E>> 中的 compareTo参数:o - 要比较的对象。返回:负整数、零或正整数,根据此对象是小于、等于还是大于指定对象。
Weather[] arrays = Weather.values();for(Weather weather2 : arrays){ System.out.println((Weather.Sunny).compareTo(weather2));}
ordinalpublic final int ordinal()返回枚举常量的序数(它在枚举声明中的位置,其中初始常量序数为零)。 大多数程序员不会使用此方法。它被设计用于复杂的基于枚举的数据结构,比如 EnumSet 和 EnumMap。返回:枚举常量的序数
for(Weather weather2 : arrays){ System.out.println(weather2.ordinal() + ":" + weather2);}
public abstract class EnumSet<E extends Enum<E>>extends AbstractSet<E>implements Cloneable, Serializable
与枚举类型一起使用的专用 Set 实现。枚举 set 中所有键都必须来自单个枚举类型,该枚举类型在创建 set 时显式或隐式地指定。枚举 set 在内部表示为位向量。此表示形式非常紧凑且高效。此类的空间和时间性能应该很好,足以用作传统上基于 int 的“位标志”的替换形式,具有高品质、类型安全的优势。如果其参数也是一个枚举 set,则批量操作(如 containsAll 和 retainAll)也应运行得非常快。
此类可以帮助我们建立枚举值的集合,里面提供了一系列的静态方法,可以指定不同的集合建立方式。
public static <E extends Enum<E>> EnumSet<E> of(E first, E... rest)创建一个最初包含指定元素的枚举 set。此工厂方法的参数列表使用变量参数功能,该方法可以创建一个最初包含任意个元素的枚举 set,但是这样很可能比不使用变量参数的重载运行得慢。参数:first - 此 set 最初要包含的元素rest - 此 set 最初要包含的其余元素返回:最初包含指定元素的枚举 set抛出:NullPointerException - 如果任意参数为 null,或 rest 为 null
EnumSet<Weather> enumSet = EnumSet.of(Weather.Sunny, Weather.Rainy);for(Iterator<Weather> iter = enumSet.iterator(); iter.hasNext();){ System.out.println(iter.next());}
public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType)创建一个具有指定元素类型的空枚举 set。参数:elementType - 此枚举 set 的元素类型的 class 对象抛出:NullPointerException - 如果 elementType 为 null
EnumSet<Weather> enumSet2 = EnumSet.noneOf(Weather.class);enumSet2.add(Weather.Sunny);enumSet2.add(Weather.Rainy);for(Iterator<Weather> iter = enumSet2.iterator(); iter.hasNext();){ System.out.println(iter.next());}
List<Weather> list = new ArrayList<Weather>();list.add(Weather.Sunny);list.add(Weather.Cloudy);for(Iterator<Weather> iter = list.iterator(); iter.hasNext();){ System.out.println(iter.next());}
public class EnumMap<K extends Enum<K>,V> extends AbstractMap<K,V> implements Serializable, Cloneable
与枚举类型键一起使用的专用 Map 实现。枚举映射中所有键都必须来自单个枚举类型,该枚举类型在创建映射时显式或隐式地指定。枚举映射在内部表示为数组。此表示形式非常紧凑且高效。
枚举映射根据其键的自然顺序 来维护(该顺序是声明枚举常量的顺序)。在 collection 视图(keySet()、entrySet() 和 values())所返回的迭代器中反映了这一点。
EnumMappublic EnumMap(Class<K> keyType)创建一个具有指定键类型的空枚举映射。参数:keyType - 此枚举映射的键类型的 class 对象抛出:NullPointerException - 如果 keyType 为空
Map<Weather, String> enumMap = new EnumMap<Weather, String>(Weather.class);enumMap.put(Weather.Sunny, "晴天");enumMap.put(Weather.Rainy, "雨天");
public static String getString(Weather weather){ if(weather == Weather.Sunny){ return Weather.Sunny.getValue(); } else if(weather == Weather.Rainy){ return Weather.Rainy.getValue(); } else if(weather == Weather.Cloudy){ return Weather.Cloudy.getValue(); } return "不符合的天气情况";}
调用上面的方法:
Weather weather3 = Weather.Sunny;System.out.println(getString(weather3));
如果要使用静态变量或者方法,必须给出该方法所在的类。而使用静态导入可以使被导入类的所有静态变量和静态方法在当前类直接可见,这样就无需给出他们的类名了:
如加入以下导入语句:
import static java.util.EnumSet.noneOf;
就可以直接使用这个静态方法了:
EnumSet<Weather> enumSet2 = noneOf(Weather.class);enumSet2.add(Weather.Sunny);enumSet2.add(Weather.Rainy);
Java笔记:枚举类
1.一个类的实例是有限且固定的,这个类称为枚举类。比如季节类,只有四个对象(春、夏、秋、冬)
2.手动实现一个枚举类
(1)通过private将构造器隐藏起来
(2)把这个类的所有可能实例都使用private static final修饰的类变量来保存。
(3)如果有必要,可以提供一些静态方法。
package cn.it.lsl;public class Season { private final String name; private final String desc; private Season(String name, String desc){ this.name = name; this.desc = desc; } public static final Season SPRING = new Season("春天","踏青"); public static final Season SUMMER = new Season("夏天","夏日炎炎"); public static final Season FAIL = new Season("秋天","秋高气爽"); public static final Season WINTER = new Season("冬天","白雪皑皑"); public String getName(){ return this.name; } public String getDesc(){ return this.desc; }}
package cn.it.lsl;public class SeasonTest { public SeasonTest(Season s){ System.out.println(s.getName() + "," + s.getDesc()); } public static void main(String[] args) { new SeasonTest(Season.FAIL); }}
Season类是一个不可变类。Season类中包含了4个static final常量的Field,这4个常量Field就代表了该类所能创建的对象。当程序需要调用Season对象时,就可以通过Season.SPRING的方式来取得Season对象。
这里顺便复习一下不可变类
不可变类:创建该类的实例后,该实例的Field是不可改变的。
如果要创建自定义的不可变类,需遵循如下规则:
(1)使用private和final修饰符来修饰该类的Field。
(2)提供带参数的构造函数,用于根据传入参数来初始化类里的Field。
(3)仅为该类的Field提供getter方法,不要为该类的Field提供setter方法。
(4)如果有必要,重写Object类的hashCode和equals方法。
3.枚举类
(1)使用enum关键字定义枚举类。枚举类一样可以有自己的Field、方法,可以实现一个或多个接口,也可以有自己的构造器。
(2)使用eunm定义的枚举类默认继承了java.lang.Enum类,而不是继承Object类。
(3)使用enum定义、非抽象的枚举类默认会使用final修饰,因此枚举类不能派送子类。(并不是所有的枚举类都使用final修饰,如抽象枚举类)
(4)枚举类所有实例必须在枚举类的第一行显示列出,否则这个枚举类永远不能产生实例。
(5)所有枚举类都提供一个values方法,该方法可以方便地遍历所有枚举值。
package cn.lsl;public enum SeasonEnum { SPRING,SUMMER,FALL,WINTER;}
package cn.lsl;public class EnumTest { public void judge(SeasonEnum s){ switch(s){ case SPRING: System.out.println("春天"); break; case SUMMER: System.out.println("夏天"); break; case FALL: System.out.println("秋天"); break; case WINTER: System.out.println("冬天"); break; } } public static void main(String[] args) { //列出所有枚举类的实例 for(SeasonEnum s : SeasonEnum.values()){ System.out.println(s); } new EnumTest().judge(SeasonEnum.FALL); }}
4.枚举类的Field、方法
枚举类可以定义自己的Field和方法。
package cn.lsl;public enum Gender { MALE,FEMALE; public String name;}
package cn.lsl;public class GenderTest { public static void main(String[] args) { //通过Enum的valueOf方法来获取指定枚举类的枚举值 Gender g = Enum.valueOf(Gender.class, "FEMALE"); g.name = "女"; System.out.println(g + "," + g.name); }}
在上面程序中产生Gender对象的方式与普通类不同,而是采用通过Enum的valueOf方法来获取指定枚举类的枚举值,枚举类的实例只能是枚举值,而不是随意通过new来创建的。
其实上面程序中,没有实现java的良好封装,因为name的访问权限是public,这样就会出现随意对name赋值的情况,应该通过方法来控制对name的访问。
改造上面的程序
package cn.lsl;public enum Gender { MALE,FEMALE; public String name; public void setName(String name){ switch(this){ case MALE: if(name.equals("男")){ this.name = name; }else{ System.out.println("参数错误"); return; } break; case FEMALE: if(name.equals("女")){ this.name = name; }else{ System.out.println("参数错误"); return; } break; } } public String getName(){ return this.name; }}
package cn.lsl;public class GenderTest { public static void main(String[] args) { //通过Enum的valueOf方法来获取指定枚举类的枚举值 Gender g = Enum.valueOf(Gender.class, "FEMALE"); g.setName("女"); System.out.println(g + "," + g.getName()); g.setName("男"); System.out.println(g + "," + g.getName()); }}
定义枚举类,以上的做法还是做的不够好,枚举类通常应该设计不可变类。其Field值不应该允许改变,这样会更安全。
所以枚举类的Field都应该使用private final修饰。
package cn.lsl;public enum Gender { MALE("男"),FEMALE("女"); private final String name; private Gender(String name){ this.name = name; } public String getName(){ return this.name; }}
5.实现接口的枚举类
枚举类可以实现一个或多个接口。
package cn.it.lsl;public interface GenderDesc { void info();}
package cn.it.lsl;public enum Gender implements GenderDesc{ MALE("男"),FEMALE("女"); private final String name; private Gender(String name){ this.name = name; } public String getName(){ return this.name; } @Override public void info() { // TODO Auto-generated method stub System.out.println("枚举类实现接口"); }}
以上在枚举类里面实现接口的方法时,每个枚举值在调用该方法的时候都会有相同的行为(即方法体相同)。
如果需要每个枚举值在调用该方法时表现出不同的行为方式,应该让每个枚举值分别实现该方法。
package cn.it.lsl;public enum Gender implements GenderDesc{ MALE("男"){ public void info() { // TODO Auto-generated method stub System.out.println("枚举类实现接口,男"); } }, FEMALE("女"){ public void info() { // TODO Auto-generated method stub System.out.println("枚举类实现接口,女"); } }; private final String name; private Gender(String name){ this.name = name; } public String getName(){ return this.name; }}
6.包含抽象方法的枚举类
在枚举类中定义抽象方法的时候不能使用abstract关键字将枚举类定义成抽象类(因为系统会自动为它添加abstract关键字),因为枚举类需要显示创建枚举值,而不是作为父类,所以定义每个枚举值时必须为抽象方法提供实现。
以下一个程序在枚举类中定义了一个抽象方法,这个抽象方法由不同的枚举值提供不同的实现
package cn.it.lsl;public enum Operation { PLUS{ @Override public double eval(double x, double y) { // TODO Auto-generated method stub return x + y; } }, MINUS{ @Override public double eval(double x, double y) { // TODO Auto-generated method stub return x - y; } }, TIMES{ @Override public double eval(double x, double y) { // TODO Auto-generated method stub return x * y; } }, DIVIDE{ @Override public double eval(double x, double y) { // TODO Auto-generated method stub return x / y; } }; public abstract double eval(double x, double y); public static void main(String[] args) { System.out.println(Operation.PLUS.eval(2, 3)); System.out.println(Operation.MINUS.eval(2, 3)); System.out.println(Operation.TIMES.eval(2, 3)); System.out.println(Operation.DIVIDE.eval(2, 3)); }}
八 泛型学习
1 泛型类
1.1普通泛型
01
package
test.lujianing;
02
/**
03
* 泛型类
04
* @param <T>
05
*/
06
class
Test<T>{
07
private
T obj;
08
public
void
setValue(T obj){
09
this
.obj =obj;
10
}
11
public
T getValue(){
12
System.out.println(obj.getClass().getName());
13
return
obj;
14
}
15
}
16
/**
17
* 测试泛型类
18
*/
19
public
class
TestOne {
20
public
static
void
main(String[] args) {
21
//测试Integer泛型
22
Test<Integer> t1 =
new
Test<Integer>();
23
t1.setValue(
5
);
24
Integer i = t1.getValue();
25
System.out.println(i);
26
//测试Double泛型
27
Test<Double> t2 =
new
Test<Double>();
28
t2.setValue(
5
.55D);
29
Double d = t2.getValue();
30
System.out.println(d);
31
//测试String泛型
32
Test<String> t3 =
new
Test<String>();
33
t3.setValue(
"hello world"
);
34
String str =t3.getValue();
35
System.out.println(str);
36
}
37
}
1
java.lang.Integer
2
5
3
java.lang.Double
4
5.55
5
java.lang.String
6
hello world
1.2 K/V泛型
01
package
test.lujianing;
02
import
java.util.HashMap;
03
import
java.util.Map;
04
05
/**
06
* Created by Administrator on 14-3-30.
07
*/
08
class
TestKV<K,V>{
09
private
Map<K,V> map=
new
HashMap<K, V>();
10
public
void
put(K k, V v) {
11
map.put(k,v);
12
}
13
public
V get(K k) {
14
return
map.get(k);
15
}
16
}
17
public
class
TestFour{
18
public
static
void
main(String[] args) {
19
TestKV<String,String> t =
new
TestKV<String, String>();
20
t.put(
"name"
,
"jianing"
);
21
System.out.println(t.get(
"name"
));
22
23
TestKV<String,Integer> t2 =
new
TestKV<String, Integer>();
24
t2.put(
"age"
,
24
);
25
System.out.println(t2.get(
"age"
));
26
}
27
}
输出结果:
1
jianing
2
24
2.泛型接口
1
package
test.lujianing;
2
/**
3
* 泛型接口
4
* @param <T>
5
*/
6
public
interface
TestImpl<T> {
7
public
void
setValue(T t);
8
public
T getValue();
9
}
01
package
test.lujianing;
02
03
/**
04
* 泛型接口 Integer实现
05
*/
06
class
One
implements
TestImpl<Integer>{
07
private
Integer i;
08
public
void
setValue(Integer integer) {
09
i =integer;
10
}
11
12
public
Integer getValue() {
13
return
i;
14
}
15
}
16
17
/**
18
* 泛型接口String实现
19
*/
20
class
Two
implements
TestImpl<String>{
21
private
String str;
22
public
void
setValue(String s) {
23
str =s;
24
}
25
26
public
String getValue() {
27
return
str;
28
}
29
}
30
31
/**
32
* 泛型接口测试
33
*/
34
public
class
TestTwo {
35
public
static
void
main(String[] args) {
36
TestImpl one =
new
One();
37
one.setValue(
1
);
38
System.out.println(one.getValue());
39
40
TestImpl two =
new
Two();
41
two.setValue(
"hello word"
);
42
System.out.println(two.getValue());
43
}
44
}
输出结果:
1
1
2
hello word
3.泛型方法
01
package
test.lujianing;
02
03
/**
04
* 泛型方法类
05
*/
06
class
TestMethod{
07
/**
08
* 泛型方法
09
*/
10
public
<T>T getValue(Object s,Class<T> clazz) {
11
System.out.println(clazz.getName());
12
T t =
null
;
13
if
(clazz.getName().equals(
"java.lang.Integer"
)){
14
Double d = Double.parseDouble(s.toString());
15
int
i =d.intValue();
16
t=(T)
new
Integer(i);
17
}
18
if
(clazz.getName().equals(
"java.lang.Double"
)){
19
t=(T)
new
Double(s.toString());
20
}
21
return
t;
22
}
23
}
24
25
/**
26
* 泛型方法测试类
27
*/
28
public
class
TestThree {
29
public
static
void
main(String[] args) {
30
TestMethod t =
new
TestMethod();
31
int
i =t.getValue(
"30.0011"
,Integer.
class
);
32
System.out.println(i);
33
double
d =t.getValue(
"40.0022"
,Double.
class
);
34
System.out.println(d);
35
}
36
}
输出结果:
1
java.lang.Integer
2
30
3
java.lang.Double
4
40.0022
4.限制泛型
在上面的例子中,没有限制class Test<T>类型持有者T的范围,默认限定类型相当于Object。比如我们要限制T为数字接口类型。只需要这么做:class Test<T extends Number>,这样类中的泛型T只能是Number接口的实现类,传入非Number接口编译会出错。
5.通配泛型
01
package
test.lujianing;
02
import
java.util.HashMap;
03
import
java.util.Map;
04
/**
05
* 通配泛型
06
*/
07
public
class
TestFive {
08
public
static
void
main(String[] args) {
09
Map<String,Class<?
extends
Number>> map =
new
HashMap<String,Class<?
extends
Number>>();
10
map.put(
"Integer"
,Integer.
class
);
11
map.put(
"Double"
,Double.
class
);
12
13
for
(Map.Entry<String,Class<?
extends
Number>> entry : map.entrySet()) {
14
System.out.println(
"key:"
+ entry.getKey() +
" value:"
+ entry.getValue());
15
}
16
}
17
}
输出结果:
1
key:Double value:
class
java.lang.Double
2
key:Integer value:
class
java.lang.Integer
简单的例子:针对1.1的例子
01
public
static
void
main(String[] args) {
02
//测试Integer泛型
03
Test<Integer> t1 =
new
Test<Integer>();
04
t1.setValue(
5
);
05
fun(t1);
06
//测试Double泛型
07
Test<Double> t2 =
new
Test<Double>();
08
t2.setValue(
5
.55D);
09
fun(t2);
10
}
11
public
static
void
fun(Test<?> t){
12
System.out.println(
"通配泛型"
+t.getValue());
13
}
1
java.lang.Integer
2
通配泛型
5
3
java.lang.Double
4
通配泛型
5.55
6.补充
在泛型中,可能会遇到<T><E>等,T和E分别是Type和Element的首字母。E一般用来表示集合类型中的元素的类型,例如List接口的定义,public interface List<E> extends Collection<E>。这只是一种命名习惯,两者没有本质区别。
发现这片博文也很好:推荐一下http://my.oschina.net/caiyuan/blog/14109
- Java 基础知识巩固(二)--持续更新
- java基础知识的巩固(无序 持续更新)
- java基础知识,持续更新
- Java 基础知识巩固(一)
- Java基础知识巩固(一)
- JAVA基础知识(持续更新ing)
- Java基础知识讲解(持续更新)
- 编程基础知识(持续更新)
- java基础知识巩固(继承、构造方法)
- java基础知识巩固
- 复习巩固java基础知识
- java基础知识巩固
- Java 基础知识巩固
- java 基础知识巩固(一)
- JAVA基础知识学习笔记(持续更新中。。)
- JAVA基础知识笔记整理【持续更新】
- python基础知识巩固(二)——数据处理
- java基础巩固之反射(二)
- C语言互斥锁-条件变量实现公共缓存区数据读写
- 软件包 java.util.concurrent 的描述
- SEO 工具大全
- nodejs+mongodb实现curd 登录 注销 分页等功能的实现过程
- hdu 4472 Count DP 递推
- Java 基础知识巩固(二)--持续更新
- uva 10129
- 1053. Path of Equal Weight (30)-PAT
- 入门经典-p130-倒水问题代码解析
- IOS 项目名称修改(XCODE4.6)
- IP加速器 加速各种网络游戏.IP加速器构建的高速虚拟网络
- 项目管理之沟通和计划
- 单独启动tomcat
- 强连通分量Tarjan模板