#
异常机制综述
在运行过程中,应用程序可能遭遇各种严重程度不同的问题.异常提供了一种在不弄乱程序的情况下检查错误的巧妙方式.它也提供了一种直接报告错误的机制,而不必检查标志或者具有此作用的域.异常把方法能够报告的错误作为方法约定的一个显式部分.
异常能够被程序员看到,由编译器检查,并且由重载方法的子类保留.
如果遇到意外的错误将抛出异常,然后异常被方法调用栈上的子句捕获.如果异常未被捕获,将导致执行线程的终止.
异常的体系结构
毫无疑问,在java中异常是对象,它必定继承Throwable及其子类.Throwable中含有一个用于描述异常的字符串.Exception是Throwable的一个最常用子类,另一个子类是Error.而RuntimeException继承自Exception.
异常的种类
非检查型异常(Unchecked Exception):
非检查型异常反映了程序中的逻辑错误,不能从运行中合理恢复.
标准的运行时异常和错误构成非检查型异常,它们继承自RuntimeException和Error.
非检查型异常不用显示进行捕获.
检查型异常(Checked Exception):
这种异常描述了这种情况,虽然是异常的,但被认为是可以合理发生的,如果这种异常真的发生了,必须调用某种方法处理.
Java异常大多是检查型异常,继承自Exception类,你自己定义的异常必须是继承Exception的检查型异常.
检查型异常必须进行显示捕获.
自定义异常
继承Exception即可定义自己的异常,以下是一种常见写法
public class DBXmlFileReadException extends Exception{
public DBXmlFileReadException(String msg){
super(msg);
}
}
抛出异常
在Java语句中,可以用throw语句抛出异常,如throw new NoSuchElementException();
抛出的对象必须是Throwable类的子类型.
抛出异常的策略:
1) 如果抛出后不可能得到处理,可以抛出Error.
2) 如果你想让其它类自由选择是否处理这个异常,就可以抛出RuntimeException.
3) 如果你要求类的用户必须处理这个异常,则可以抛出Exception.
异常抛出后的控制权转移
一旦发生异常,异常发生点后的动作将不会发生.此后将要发生的操作不是在catch块和finally块.
当异常抛出时,导致异常发生的语句和表达式就被称为突然完成.语句的突然完成将导致调用链逐渐展开,直到该异常被捕获.
如果该异常没有捕获,执行线程将中止.
Try,catch和finally
异常由包含在try块中的语句捕获:
try{
正常执行语句
}
catch(XException e){
异常执行语句一
}
catch(XXException e){
异常执行语句二
}
catch(XXXException e){
异常执行语句三
}
finally{
中止语句
}
Try中的语句体要么顺利完成,要么执行到抛出异常.
如果抛出异常,就要找出对应于异常类或其父类的catch子句,如果未能找到合适的catch子句,异常就从try语句中扩散出来,进入到外层可能对它进行处理的try语句.
Catch子句可以有多个,只要这些子句捕获的异常类型不同.
如果在try中有finally子句,其代码在try把所有其它处理完成之后执行.
无论是正常完成或是出现异常,甚至是通过return或者break这样的控制语句结束,finally子句总是被执行.
Catch子句和finally子句在try语句之后至少有一个,不要求全部出现.
More…
在catch语句中捕获通用的异常Exception通常不是最佳策略,因为它会将所有异常进行等同处理.
不能把基类异常的catch语句放到子类异常的catch语句之前,编译器会在运行之前就检查出这样的错误.
Try…catch对每个catch语句都从头到尾检查,如果找到处理同类异常的catch子句,此catch块中的语句将得以执行,而不再处理同层次的其它catch块.
如果catch或finally抛出另一个异常,程序将不会再去检查try的catch子句.
Try...catch语句可以嵌套,内层抛出的异常可被外层处理.
Throws子句
函数能抛出的检查型异常用throws声明,它后面可以是带用逗号隔开的一系列异常类型.仅仅那些在方法中不被捕获的异常必须列出.
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
// Read in any hidden serialization magic
s.defaultReadObject();
// Read in size
int size = s.readInt();
// Initialize header
header = new Entry<E>(null, null, null);
header.next = header.previous = header;
// Read in all elements in the proper order.
for (int i=0; i<size; i++)
addBefore((E)s.readObject(), header);
}
}
More…
Throws子句的约定是严格强制性的,只能抛出throws子句中声明的异常类型,抛出其它类型的异常是非法的,不管是直接利用throw,还是调用别的方法间接的抛出.
RuntimeException和Error是仅有的不必由throws子句列出的异常.
调用函数的函数要么处理对声明的异常进行处理,要么也声明同样的异常,将收到的异常抛向上层.
对检查型异常通常进行的几种处理
1) 用e.printStackTrace()输出异常信息.
2) 将异常记录到日志中以备查,如logger.error(e.getMessage()).
3) 试图进行异常恢复.
4) 告知维护者和用户发生的情况.
内部类的出现
当进行Java开发时,有时需要实现一个仅包含1-2个方法的接口.在AWT和Swing开发中经常出现这种情况,例如当一个display组件需要一个事件回调方法如一个按钮的ActionListener时. 如果使用普通的类来实现此操作,最终会得到很多仅在单个位置上使用的小型类.
内部类用于处理这种情况,java允许定义内部类,而且可在Gui外使用内部类.
内部类的定义和实现
内部类是指在另一个类内部定义的一个类.可以将内部类定义为一个类的成员.
public class Linker{
public class LinkedNode{
private LinkedNode prev;
private LinkedNode next;
private String content;
public LinkedNode(String content){
this.content=content;
}
}
public Linker(){
LinkedNode first=new LinkedNode("First");
LinkedNode second=new LinkedNode("Second");
first.next=second;
second.prev=first;
}
}
定义在一个类方法中的内部类
public class Hapiness{
interface Smiler{
public void smile();
}
public static void main(String[] args){
class Happy implements Smiler{
public void smile(){
System.out.println(":-}");
}
}
Happy happy=new Happy();
happy.smile();
}
}
匿名类
对很多情况而言,定义在方法内部的类名意义不大,它可以保持为匿名的,程序员关心的只是它的实例名.
如:
Runnable runner=new Runnable(){
public void run(){
// Run statememnt
}
}
理解匿名类
匿名类并不难理解,它只是把类的定义过程和实例的创建过程混合而已,上页的语句实际上相当于如下语句:
// 定义类
Public class Runner implements Runnable{
public void run(){
// do sth
}
}
// 创建实例
Runner runner=new Runner();
使用匿名类的筛选解耦过程
需求:从公司的职员列表中,找出男性且年龄大于22的成员.
传统写法:
List allmembers=company.getMembers();// 取得所有成员
List results=new ArrayList();// 结果列表
for(Iterator it=allmembers.iterator();it.hasNext();){
Member member=(Member)it.next();
if(member.getAge()>22 && member.isMale()){ // 筛选,这里是把查询条件和遴选过程融合在一起,条件一变立即就得加个分支.
results.add(member);
}
}
传统方法的缺陷
这种写法没有错,但是不是面向对象的写法,它有以下缺陷:
1.查询条件和筛选过程没有分离.
2.这样写的后果使Company变成了一个失血模型而不是领域模型.
3.换查询条件的话,上面除了"筛选"一句有变化外其它都是模板代码,重复性很高.
使用匿名类实现的OO化查询
真正符合OO的查询应该是这样:
MemberFilter filter1=new MemberFilter(){
public boolean accept(Member member) {
return member.isMale() && member.getAge()>22;
}
};
List ls=company.listMembers(filter1);
这段代码成功的把查询条件作为一个接口分离了出去,接口代码如下:
public interface MemberFilter{
public boolean accept(Member member);
}
查询函数的变化
而类Company增加了这样一个函数:
public List searchMembers(MemberFilter memberFilter){
List retval=new ArrayList();
for(Iterator it=members.iterator();it.hasNext();){
Member member=(Member)it.next();
if(memberFilter.accept(member)){
retval.add(member);
}
}
return retval;
}
这就把模板代码归结到了类内部,外面不会重复书写了.Company也同时拥有了数据和行为,而不是原来的数据容器了.
匿名类的例子二
用匿名类处理分类汇总的方法 分类汇总是统计中常用,举例来说如统计学生成绩,及格不及格的归类,分优良中差等级归类等,每个单项代码很好写,但是如果分类汇总的项目多了,能一种汇总写一个函数吗? 比如说有些科目60分才算及格,有些科目50分就算;有些老师喜欢分优良中差四等,有些老师却喜欢分ABCD;不一而足,如果每个都写一个函数无疑是个编写和维护恶梦. 如果我们用匿名类把分类汇总的规则和分类汇总的过程分别抽象出来,代码就清晰灵活多了,以下代码讲述了这个过程.
基本类Student
public class Student{
private String name;
private int score;
public Student(String name,int score){
this.name=name;
this.score=score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
}
用于分类汇总的类
它强制子类实现getKey和getvalue两个方法:
public abstract class ClassifyRule {
public Student student;
public ClassifyRule(){
}
public void setStudent(Student student) {
this.student = student;
}
abstract public String getKey();
abstract public int getValue();
}
对Student进行统计处理的StudentService类
注意getSum方法,它保留了筛选过程,筛选规则则不在其中:
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
public class StudentService {
private List<Student> students;
public StudentService() {
students = new ArrayList<Student>();
}
public void add(Student student) {
students.add(student);
}
public Hashtable<String, Integer> getSum(ClassifyRule rule) {
Hashtable<String, Integer> ht = new Hashtable<String, Integer>();
for (Student student : students) {
rule.setStudent(student);
String key = rule.getKey();
int value = rule.getValue();
if (ht.containsKey(key)) {
Integer oldValue = ht.remove(key);
oldValue += value;
ht.put(key, oldValue);
} else {
ht.put(key, value);
}
}
return ht;
}
}
测试代码,注意其中筛选规则的创建
public class Test {
public static void main(String[] args) {
// 初始化
StudentService service = new StudentService();
service.add(new Student("Andy", 90));
service.add(new Student("Bill", 95));
service.add(new Student("Cindy", 70));
service.add(new Student("Dural", 85));
service.add(new Student("Edin", 60));
service.add(new Student("Felix", 55));
service.add(new Student("Green", 15));
// 60分及格筛选
ClassifyRule rule60 = new ClassifyRule() {
public String getKey() {
return student.getScore() >= 60 ? "及格" : "不及格";
}
public int getValue() {
return 1;
}
};
System.out.println("60分及格筛选");
printHt(service.getSum(rule60));
// 50分及格筛选
ClassifyRule rule50 = new ClassifyRule() {
public String getKey() {
return student.getScore() >= 50 ? "及格" : "不及格";
}
public int getValue() {
return 1;
}
};
System.out.println("\n50分及格筛选");
printHt(service.getSum(rule50));
// 分"优良中差"等级
ClassifyRule ruleCn = new ClassifyRule() {
public String getKey() {
String retval = "";
int score = student.getScore();
if (score >= 90) {
retval = "优";
} else if (score >= 80) {
retval = "良";
} else if (score >= 60) {
retval = "中";
} else if (score > 0) {
retval = "差";
}
return retval;
}
public int getValue() {
return 1;
}
};
测试代码
System.out.println("\n分优良中差等级筛选");
printHt(service.getSum(ruleCn));
// 分"ABCD"等级
ClassifyRule ruleWest = new ClassifyRule() {
public String getKey() {
String retval = "";
int score = student.getScore();
if (score >= 90) {
retval = "A";
} else if (score >= 80) {
retval = "B";
} else if (score >= 60) {
retval = "C";
} else if (score > 0) {
retval = "D";
}
return retval;
}
public int getValue() {
return 1;
}
};
System.out.println("\n分ABCD等级筛选");
printHt(service.getSum(ruleWest));
}
private static void printHt(Hashtable ht) {
for (Iterator it = ht.keySet().iterator(); it.hasNext();) {
String key = (String) it.next();
Integer value = (Integer) ht.get(key);
System.out.println("Key=" + key + " Value=" + value);
}
}
}
测试结果如下:
60分及格筛选
Key=及格 Value=5
Key=不及格 Value=2
50分及格筛选
Key=及格 Value=6
Key=不及格 Value=1
分优良中差等级筛选
Key=优 Value=2
Key=良 Value=1
Key=中 Value=2
Key=差 Value=2
分ABCD等级筛选
Key=A Value=2
Key=D Value=2
Key=C Value=2
Key=B Value=1
小结
内部类也叫嵌套类,一般不提倡书写,但它在java核心类中都存在,如接口Map中的Entry,我们应该了解并能解读这种方法.
匿名类相对而言有用得多,在解耦合和事件回调注册中很常见,大家应该对它的运用融会贯通.
Abstract class:抽象类
Abstract method:抽象方法
Annotation:注释
Anonymous class:匿名类
API(Application Programming Interface):应用编程接口,由方法和语言构成的库.
ArrayList:实现了List接口的动态数组
Assertion:断言
Atrribute map:属性映射
Autoboxing:自动装箱,表示一个内置类型如int和它的包装类如Integer之间的自动转换
Boolean function:布尔函数
Bytecode:字节码
Casting:类型强制转换
Channel:频道
ClassCastException:当一个对象引用强制转换程一个不兼容的类型时出现的异常.
Collection:一个表示对象组或集合的接口
CSV(Comma-separated values):一种用于存储表格数据的文件形式
Complier:编译器
Compose:合成
Composite function:复合函数,通过多个函数创建的一个函数
Decimal:十进制
Deep:深度
DOM(Document Object Model):文档对象模型,一种采用树形表示来处理XML数据的API.
Database:数据库
Edge:边
Element:元素,XML文档中的一个节点.
Encapsulation:封装
End tag:结束标签
Enum:枚举
Escaping:转义
Function:函数
Fuzzy search:模糊搜索
Generic:泛型
Graph:图
GUI:用户图形界面
Grid computing:网格计算
Group:组
HashMap:一个将键值映射到值的查找对象
Heap memory:堆内存
HTML(Hyper Text Markup Language):超文本标记语言
HTTP(HyperText Tranfer Protocol):超文本传输协议
Inheritance:继承
Inner class:内部类
Iterator:允许迭代到任何Collection类的一个接口
JDBC(Java Database Connectivity):java数据库连接,一种属于Java核心库的用于操作关系数据库的API.
JDK(Java Development Kit):java开发工具包
JRE(Java Runtime Environment):java运行时环境
JSP(Java Server Page):java服务页
JVM(Java Virtual machine):Java虚拟机
JavaDoc:属于JDK的一种文档生成工具
JavaScript:运行于客户端,用于HTML处理的一种轻量级教本语言,语法部分类似于Java.
Layout:布局
Lexical analysis:词法分析
Linked List:链表
Matcher:一个用于正则表达式模式匹配的Java类
Metadata:元数据
Millisecond:微妙
Namespace:命名空间
Neural network:神经网络
Node:节点
Object-oriented programmming:面向对象编程
Object pool:可以从中获得对象的一个实例池
Origin:原点
Override:子类覆写父类的方法
Parser:分析器
Patch:补丁
Pattern:模式
Polymorphism:多态性
Port:端口
Predicate:谓词
Prefix:前缀
Procedural language:过程式语言,如C
Property:属性
Real time:实时
Recursive:递归
Reference:引用
Reflection:反射
Regular expression:正则表达式
Relative:相对
Resource:资源
Runnable:多线程编程使用的一个接口.
Syntax:语法
Screen scraping:屏幕抓取
Split:分割
State:状态
Static:静态
Sequence:序列
Swing:构建在AWT上更高级的图形用户界面
Synchronized:同步,用于线程安全协作的技术
Tag:标签
Thread:进程
Tiger : Sun给Java1.5的代号
Token:标记
Translation:平移
Triple:三元组
Type:类型
Unicode:统一字符界面
Unit testing:单元测试
Visitor pattern:访问者模式
WAR(Web Application Archive):Web应用程序归档
Web Service:Web服务
Weight:权
Well-formed:格式良好的
Whitespace:空白符
XML(Extensible Markup Language):一种描述分级数据结构的文本标记语言
编写程序前的设计与思考
1.分析业务,从业务流和业务规则中归纳出领域对象.这些对象一般放在src/com/yourname/domain下.
2.根据业务,考虑为领域对象提供完整的服务需要那些服务类.这些对象一般放在src/com/yourname/service下.
3.思考从输入开始,到输出结束,程序要运行正常,服务类需要那些属性和方法,这些成员代表什么意义具有什么价值,方法的参数的返回值分别是什么.
4.思考要让服务类为领域对象类提供完整普适的服务,领域对象类需要做怎样的调整,需要归纳那些共同的基类.这里可以绘出领域类和服务类的静态类图协助思考.
5.考虑还需要那些实用类来帮助实现程序.这些对象一般放在src/com/yourname/util下.
6.在领域类和服务类的基础上设计持久层,控制层和表现层(当前阶段暂时不会接触).
软件设计的通用原则
Domain first:先归纳程序的领域对象,归纳出来才清楚程序要做什么.
Service second:其次归纳程序的服务对象,归纳出来才知道该怎么去做.
前两步完成后可以说设计已经完成了大半.
Persistence the third:再次再考虑数据如何持久化到持久层中,比如说数据库表结构的设计.
Presentation the last:最后考虑表现层的东西,比如说界面方案.
现代软件设计的核心-类的设计
从上两步可以看出,软件设计其实就是类的设计工作,类设计的结果直接影响着软件的方方面面,所谓持久层设计,表现层设计都是类设计的泛化和深化,所以说类设计直接影响着软件的兴衰成败.
那么关于类设计的准则和评价标准是什么呢?
类设计的五条基本准则
单一职责原则(The single responsibility principle): 一个类有且只有一个中心目的,它为一个中心目的所设计,只能因为中心目的相关原因而改变.
开放-封闭原则(The open-close principle):类高度内聚,和其它类的耦合程度小,应该可以在不改变类的情况下,改变类所在的环境.
里斯科夫替换原则(The Liskov substitution principle):派生类的行为为基类所规定和限制,避免使派生类的方法非法化或者退化它们.基类的使用者不需要了解派生类就能正确的通过接口使用它们.
依赖关系倒置原则(The dependecy inversion principle):基于抽象类和接口而不是具体的类形成设计构架,因为抽象类和接口相对具体类而言更不容易被改动.
接口隔离原则(The interface segregation principle):给对象的每个使用者一个单独的接口.其中只包含它们需要的方法,不要设计一个包含很多方法的接口让不需要这些接口的类去实现它们.
类的评价标准-抽象相关
类是否有一个中心目的.
类的命名是否恰当,其名字是否表现了其中心目的.
类的接口是否展现了一致的抽象.
类的接口是否让人明白的知道该如何使用它.
类的接口是否足够抽象,是你能不必顾虑它是如何进行服务的.
类提供的服务是否足够完整,能让其它类无须了解动用其内部结构.
是否已经去除无关信息.
是否考虑过把类进一步分解成组件类?是否已经尽可能将其分解.
再修改类时是否维持了其接口的完整性.
类的评价标准--封装相关
是否把类成员的可访问性降至最小.
是否避免暴露类中的数据成员.
类是否已经尽可能的对其它类隐藏了实现细节.
类是否避免对其使用者,包括其派生类如何使用它做了假设.
类是否不依赖其它类,它是松耦合的吗?
典型的设计的臭味
僵化性(Rigidiry):类之间耦合严重,系统几乎很难改变,改变一处就不得不改动其它地方,甚至引起无休止的连锁反应.
易碎性(Fragility):改变系统的某个部分,会破坏许多完全无关的部分.这一般由不必要的语法结构引起,如过度复杂的循环和分支.
固化性(Immobility):很难将系统分解成可供其它系统使用的组件,细化不够会引起这样的问题.
粘稠性(Viscosity):开发环境总是和输入输出和库粘在一起.永远走不出编辑->编译->测试这一无休止的循环,分层不清晰会引起这样的问题.
不必要的复杂性(Needless Complexity):很多充满着智慧的代码结构目前还不需要,但有一天可能会排上用场,喜欢事先考虑幽灵需求的程序员经常给代码带来这样的异味.
不必要的重复(Needless Repetition): 系统充斥着重复代码,看上去象只会VC大法(Ctrl+C,Ctrl+V)的程序员写的,懒惰不思考是造成这个问题的根源.
不透明性(Opacity):代码不能体现创建人的意图.
何谓良好的设计
设计良好的系统应该容易理解,容易改变,容易重用.它实现起来没有任何特殊的困难,简明扼要而经济.它不会散发出代码臭味,公司乐于生产这样的系统,客户乐于使用这样的系统,维护人员乐于维护这样的系统.
设计良好的现代软件特征
最小的复杂度:整个系统可以分解为简单而易于理解的各个部分.
易于维护:程序有良好的可维护性.
松散耦合,通过应用类接口中的合理抽象,封装性以及信息隐藏等原则,设计出相互关联尽可能最少的类.
适应变化: 能在不改变系统基本构架的基础上,适应未来的变化,有良好的扩展性,程序可扩展,可重用.
有明晰的层次,每层各司其职,有良好分工.
高扇入低扇出:系统很好的利用了较低层次上的工具类,重复代码很少或没有.
有良好的规范:无论多少人参与了项目,从代码看来犹如出自一人之手.
使用标准技术.
集合框架的继承体系
Java2中包含了6个集合接口,这些接口的通用目的实现类,以及一个集合实用类Collections,共同组成了集合框架的核心.
6个集合接口如下:
Collection及其以下的List,Set,以及Set下的SortedSet
Map及其以下的SortedMap
链表的通用实现
ArrayList:线程不安全的动态数组类,批量查询速度快,单个查找速度慢.
Vector:线程安全的动态数组类,效率比ArrayList低下.
LinkedList:动态链表类,适于在队列首尾频繁增删成员的场合.
链表的创建
List<Member> ls=new ArrayList<Member>();// 1.5版本及以上
List ls=new ArrayList();// 1.5版本以下
之所以将ls类型指定为List<Member>而不是ArrayList<Member>是因为 如果情况变化, ArrayList需要修改成Vector或LinkedList时无须修改其它代码,仅修改实例类型即可.
向List中添加元素
添加单个元素
ls.add(new Member("Andy",21));
从别的链表中添加多个元素
List<Member> ls2=new ArrayList<Member>();
ls2.add(new Member("Felex",21));
ls2.add(new Member("Gates",23));
ls.addAll(ls2);
注意:
1.5及其以上版本中,添加的元素必须和定义时一致,如ls的类型是List<Member>,那么向其中添加的元素必须是Member或其子类.
1.5以下版本中,对添加的元素不作检查,只要是Object即可,如果是基本类型则需装箱成类类型.如ls.add(new Integer(8));
删除链表中元素
ls.remove(member); // 直接删除对象,member=new Member("Edin",28)
ls.remove(2); // 删除第三个元素
ls.clear(); // 将已有数据全部清除
对链表进行遍历
1) 1.5中独有的遍历方法,代码最短,使用最方便
for(Member member:ls){
// member就是依次取出的每个元素
}
2) 常规方法,多用于需要取得下标的场合,各版本均适用
for(int i=0;i<ls.size();i++){
Member member=ls.get(i);// member就是依次取出的每个元素, 1.5及其以上版本适用, ls.size()为链表长度
Member member=(Member)ls.get(i);// 1.5以下版本适用
}
3) 使用Iterator对链表进行遍历的方法,各版本均适用
for(Iterator it=ls.iterator();it.hasNext();){
Member member=(Member)it.next();
}
链表的其它常用的方法
ls.contains(Object o);// 返回链表中是否包含某元素
ls.indexOf(Object o);// 返回对象o是链表中的第几号元素
isEmpty();// 返回链表中是否有元素
对链表进行排序
1)让List中元素必须已经实现Comparable接口:
public class Member implements Comparable {
private String name;
private int age;
public Member(String name, int age) {
this.name = name;
this.age = age;
}
public int compareTo(Object obj) {
Member another = (Member) obj;
return this.name.compareTo(another.name);// 按名称排序,如果是this.age-another.age则按年龄排序
}
public String toString(){
return "Member name="+name+" age="+age;
}
}
2)排序过程
List<Member> ls=new ArrayList<Member>();
ls.add(new Member("Felex",21));
ls.add(new Member("Gates",23));
ls.add(new Member("Andy",21));
ls.add(new Member("Bill",23));
ls.add(new Member("Cindy",24));
ls.add(new Member("Dell",27));
Collections.sort(ls);
for(Member member:ls){
System.out.println(member);
}
输出:
Member name=Andy age=21
Member name=Bill age=23
Member name=Cindy age=24
Member name=Dell age=27
Member name=Felex age=21
Member name=Gates age=23
// 如果需要逆序可以使用Collections.reverse(ls);
链表与数组之间的转换
1) List转换成数组
List<String> ls=new ArrayList<String>();
ls.add("Felex");
ls.add("Gates");
ls.add("Andy");
ls.add("Bill");
ls.add("Cindy");
ls.add("Dell");
Object[] arr=ls.toArray();
for(Object obj:arr){
System.out.println((Object)obj);
}
输出为:
Felex
Gates
Andy
Bill
Cindy
Dell
2) 数组转换成List
String[] arr={"Andy","Bill","Cindy","Dell"};
List<String> ls=Arrays.asList(arr);
for(String str:ls){
System.out.println(str);
}
输出:
Andy
Bill
Cindy
Dell
Hashtable-哈希表类
以哈希表的形式存储数据,数据的形式是键值对.
特点:
查找速度快,遍历相对慢
键值不能有空指针和重复数据
创建
Hashtable<Integer,String> ht=new Hashtable<Integer,String>();
添值
ht.put(1,"Andy");
ht.put(2,"Bill");
ht.put(3,"Cindy");
ht.put(4,"Dell");
ht.put(5,"Felex");
ht.put(6,"Edinburg");
ht.put(7,"Green");
取值
String str=ht.get(1);
System.out.println(str);// Andy
对键进行遍历
Iterator it = ht.keySet().iterator();
while (it.hasNext()) {
Integer key = (Integer)it.next();
System.out.println(key);
}
对值进行遍历
Iterator it = ht.values().iterator();
while (it.hasNext()) {
String value =(String) it.next();
System.out.println(value);
}
取Hashtable记录数
Hashtable<Integer,String> ht=new Hashtable<Integer,String>();
ht.put(1,"Andy");
ht.put(2,"Bill");
ht.put(3,"Cindy");
ht.put(4,"Dell");
ht.put(5,"Felex");
ht.put(6,"Edinburg");
ht.put(7,"Green");
int i=ht.size();// 7
删除元素
Hashtable<Integer,String> ht=new Hashtable<Integer,String>();
ht.put(1,"Andy");
ht.put(2,"Bill");
ht.put(3,"Cindy");
ht.put(4,"Dell");
ht.put(5,"Felex");
ht.put(6,"Edinburg");
ht.put(7,"Green");
ht.remove(1);
ht.remove(2);
ht.remove(3);
ht.remove(4);
System.out.println(ht.size());// 3
Iterator it = ht.values().iterator();
while (it.hasNext()) {
// Get value
String value =(String) it.next();
System.out.println(value);
}
输出:
3
Green
Edinburg
Felex
类,抽象类与接口
类,抽象类与接口都是Java中实现继承和多态的手段.
类强调的是继承
接口强调的是规范
抽象类兼而有之
什么是接口
接口是一种特殊的类,它只有方法定义而没有实现,实现的任务完全交给子类完成.
接口以interface标志.
继承接口用implements关键字实现.
接口可以继承接口.
接口可以有成员,但成员全是public static final类型的.
接口没有构造函数.
接口给Java提供了多继承机制
接口例子—Knockable
public interface Knockable{
public static final String Sound="Bang!";
public void knock();
}
接口例子-实现接口
public class Hammer implements Knockable{
public void knock(){
System.out.println(Sound);
}
}
接口例子-实现多个接口
public interface Knockable{
public static final String Sound="Bang!";
public void knock();
}
public interface Clampable {
public void clamp();
}
public class Pliers implements Knockable,Clampable{
public void clamp(){
// do sth
}
public void knock(){
System.out.println(Sound);
}
}
接口继承接口例子
public interface Knockable{
public static final String Sound="Bang!";
public void knock();
}
public interface Hitable extends Knockable{
public void hit();
}
public class Stick implements Hitable{
public void knock(){
// do sth
}
public void hit(){
// do sth
}
}
什么叫抽象类
类中具有抽象方法的类为抽象类.抽象方法以abstract在函数前修饰,只有定义没有实现,这一点和接口一致.
抽象类在类名前需加上abstract修饰符以标识.
抽象类不能生成实例.
抽象类的继承性和类一致.
抽象类的实现
public abstract class Gun{
protected String cannon;
protected List<Bullet> bullets;
public abstract void shoot();
public void addBullet(Bullet bullet){
if(bullets==null){
bullets=new LinkedList<Bullet>();
}
bullets.add(bullet);
}
}
继承抽象类
public class Rifle extends Gun{
public void shoot(){
// Shoot Sth
}
public void reload(){
if(bullets!=null){
bullets.clear();
}
addBullet(new Bullet());
addBullet(new Bullet());
addBullet(new Bullet());
addBullet(new Bullet());
addBullet(new Bullet());
addBullet(new Bullet());
}
}
继承抽象类并实现接口
public abstract class Gun{
protected String cannon;
protected List<Bullet> bullets;
public abstract void shoot();
public void addBullet(Bullet bullet){
if(bullets==null){
bullets=new LinkedList<Bullet>();
}
bullets.add(bullet);
}
}
public interface Thornable{
public void thorn();
}
public class SpringFieldRifle extends Gun implements Thornable{
public void thorn(){
// thorn sth
}
public void shoot(){
// shoot sth
}
}
抽象类继承抽象类实现接口的例子
public abstract class Gun{
protected String cannon;
protected List<Bullet> bullets;
public abstract void shoot();
public void addBullet(Bullet bullet){
if(bullets==null){
bullets=new LinkedList<Bullet>();
}
bullets.add(bullet);
}
}
public interface Handable{
public void hold();
}
public abstract class HandGun extends Gun implements Handable{
}
public class BlackStar extends HandGun{
public void hold(){
// Hold the gun
}
public void shoot(){
// Shoot Sth
}
}
抽象类,接口,类的区别
继承好比家学渊源,所谓"忠厚传家久,诗书继世长",家长总会潜移默化的影响下一代,下一代也会在不经意中学习前辈的特点,但因为年少分辨能力不高加上世易时移有些优点已经不再是有点甚至会变成缺点,下一代会把前辈的优缺点不分良莠的继承下来.这也是日后出现问题的根源.
接口好比拜师学艺,"入了这个门,就得说这行话",比如相声界说学逗唱四门是必须要学的,但是"师傅领进门,修行在个人",学得怎么样还全看自己,指望不费力的继承什么是不可能的,具体功夫还得个人来过. 因为是自己来,具体实现成什么样自由度也很大,比如四门功课中的"唱",原指唱太平歌词,但因为爱听的少,现在相声演员已经不要求这个了,改为唱歌唱戏的唱,其实严格界定的话是"学"的一种.这也无所谓对错,郭德刚坚持唱太平歌词也行,笑林唱流行歌曲也不错,总之实现了就可以,实现得怎么样则留给实践来检验.一个类可以同时实现多个接口,就和艺人拜几个师傅是没有问题的,郭德刚就同时实现了大鼓和相声两个接口.
抽象类则介于继承和接口之间,既可不费力的从上一代继承,也可强制实现某接口,有如某大师收自己的孩子为徒,当然相声界不让这么干,其它曲艺行业还是可以的,比如京剧界的梅兰芳和其子梅葆玖,既有言传身教,也有强制实现,综合了继承和接口的特点.
抽象类,接口,类的详细区别
|
接口 |
抽象类 |
类 |
可以继承自 |
接口 |
抽象类,类 |
抽象类,类 |
可以实现自 |
无 |
一个或多个接口 |
一个或多个接口 |
有否成员 |
只有static final成员 |
均可 |
均可 |
是否有构造函数 |
无 |
有 |
有 |
可否实例化 |
不可 |
不可 |
可 |
意图 |
规范行为 |
继承成员+规范行为 |
继承成员 |
抽象类,接口和类在继承体系中的位置
抽象类,接口一般来说都是从一般类总结归纳出来的抽象共性的东西,类则是实际具体的东西.
一般来说,应该把合理抽象出的抽象类,接口放在类继承体系的上层,子类依靠类来实现.