#
摘要: 单类包含是指一个类是另一个类的成员变量,比如有这样两个类,个人(Person)和地址(Addr),Addr是Person的成员变量,类图如下:
两个类分别对应数据库中的Persons和Addrs表,它们的ER图如下:
具体怎么把OO对象和具体的数据库实体表无缝联系起来呢,下面的代码展示了如何把两个类映射到数据库中的表.
Person类代码:
package c...
阅读全文
摘要: 类集合包含意味着一个类中的成员变量是另一个类的集合,比如说公司类Company包含成员类Member的集合.
类图如下:
它们分别对应数据库中的Companys表和Members表,它们的ER图如下:
以下代码演示了如何将类与数据库映射起来:
Company类代码:
package com.sitinspring.companymember;
impo...
阅读全文
摘要: 多对多关系一般指两个类都拥有对方集合的成员变量,比如说文章类Article和标签类Tag,一个Arirtle类可以拥有多个Tag,一个Tag也适用于多篇文章,它们的类图如下:
它们也分别对应数据库中的实体表Articles和Tags,当然仅靠这两个表实现多对多能力是有限的,我们还需要第三个表ArticleTag的帮忙,它们的ER图如下:
实际上多对多关系并不复杂,加入一个中...
阅读全文
摘要: 本文是 "从薪水计算的例子看一段程序在不同环境中的变化 " 的续文.
如果需求发生如下变化:
如果说国家改变了公民福利制度,具体就是500元以下的每人补充300元,超过20000元的在原有基础上再扣除20%,请问该如何编程?
具体等级税率:
等级 &nb...
阅读全文
摘要: 如果有以下需求,你是一个货栈的仓库保管员,货栈进口以下多种水果,目前主要是苹果,香蕉和桔子,货栈不但需要记录每批次的品种,单价,还要得出每种水果的总个数,总钱数. 请问该如何编制程序.
记录每批次不难,一个批次链表就可以解决问题,有点意思的部分在于得出每种水果的总个数,总钱数,如果说在加入批次信息时进行处理,根据类型分别判断,用六个量(分别针对每种水果的总个数,总钱数)分别进行统计.这种方法...
阅读全文
摘要: 抽象单位类,Soldier和TroopUnit的基类:
package com.sitinspring;
/** *//**
* 抽象单位类,Soldier和TroopUnit的基类
* @author sitinspring(junglesong@gmail.com)
*
*&nbs...
阅读全文
如果你的答案是斩钉截铁的"不能",那么请你继续向下看,说不定这篇文章能对你有所用处.
首先请看两个类的代码:
BaseClass:
package com.sitinspring;
import java.util.Vector;
/**
* 基类BaseClass,ChildClass类的父类
* @author: sitinspring(junglesong@gmail.com)
* @date: 2007-12-4
*/
public class BaseClass{
// 私有动态数组成员,注意它是"private"的
private Vector objects;
/**
* 在构造函数
*
*/
public BaseClass(){
objects=new Vector();
}
/**
* 公有函数,向动态数组成员objects添加字符串
* @param str
*/
@SuppressWarnings("unchecked")
public void addStr2Obs(String str){
objects.add(str);
}
/**
* 公有函数,打印objects中的诸元素
*
*/
public void printAll(){
for(int i=0;i<objects.size();i++){
System.out.println("序号="+i+"\t元素="+objects.get(i));
}
}
}
ChildClass,BaseClass的派生类:
package com.sitinspring;
/**
* ChildClass,BaseClass的派生类
* @author: sitinspring(junglesong@gmail.com)
* @date: 2007-12-4
*/
public class ChildClass extends BaseClass{
public void printObjects(){
// 下面的句子是不能编译通过的
/*for(int i=0;i<objects.size();i++){
System.out.println("序号="+i+"\t元素="+objects.get(i));
}*/
}
public static void main(String[] args){
ChildClass childClass=new ChildClass();
childClass.addStr2Obs("Hello");
childClass.addStr2Obs("World");
childClass.addStr2Obs("China");
childClass.addStr2Obs("sitinspring");
childClass.printAll();
}
}
再让我们把断点停在main函数中的childClass.printAll()上,看看实例childClass中到底有什么.
以上截图证明:
objects确实是ChildClass类实例childClass的成员,而且四个字符串也都被加进去了.
最后执行出来,结果如下:
序号=0 元素=Hello
序号=1 元素=World
序号=2 元素=China
序号=3 元素=sitinspring
这也说明,上面红字部分的论断是正确的.
再翻看书籍,关于private限制的成员变量是这样写的:
private 只允许来自改类内部的方法访问.不允许任何来自该类外部的访问.
我们上面添字符串和遍历输出函数都是BaseClass的成员,所以它当然被这两个函数访问.而ChildClass的printObjects是BaseClass类外部的函数,结果当然是编译也不能通过.
实际上,private,public,protected和继承没有关系,他们对成员函数和变量的限制只是在成员的可见性上,
public允许来自任何类的访问;
private只允许来自改类内部的方法访问,不允许任何来自该类外部的访问;
protected允许来自同一包中的任何类以及改类的任何地方的任何子类的方法访问.
而关于成员变量的继承,
父类的任何成员变量都是会被子类继承下去的,私有的objects就是明证,这些继承下来的私有成员虽对子类来说不可见,但子类仍然可以用父类的函数操作他们.
这样的设计有何意义呢?我们可以用这个方法将我们的成员保护得更好,让子类的设计者也只能通过父类指定的方法修改父类的私有成员,这样将能把类保护得更好,这对一个完整的继承体系是尤为可贵的. jdk源码就有这样的例子,java.util.Observable就是这样设计的.
本文例子下载:
http://www.blogjava.net/Files/sitinspring/PrivatePuzzle20071204210542.rar
首先解释一下,文本中的信息指的是 对象在文本文件中的描述,如"名称:Bill 职位:SSE 年龄:45 薪水:10000"这个形式的.要求把这样的信息转换到对象Member中,对录入出错的情况如年龄薪水有非数字字符需要加以鉴别.
对象基本信息如下:
public class Member implements Comparable{
// 名称
private String name;
// 年龄
private int age;
// 职位
private String title;
// 薪水
private int salary;
.
}
从这段字符串中找到相关的信息并设置到Member对象的相关属性中并不难,但有几个地方需要多加考虑:
1.名称职位薪水年龄的顺序不一定一致.
2.职位薪水年龄三个字段和值有可能没有.
3.有可能需要增加字段,此时类也需要修改.
处理程序需要考虑解析,验证,赋值三个环节,如果耦合在一起处理当然也能做出来,但这样做可读性和可维护性都不好,也背离了面向对象的初衷.好的方案应该把这三部分分开制作函数处理.
文本解析部分:
我的想法是首先将"名称:Bill 职位:SSE 年龄:45 薪水:10000"以空格劈分成包含这样元素的链表:
名称:Bill
职位:SSE
年龄:45
薪水:10000
然后在用冒号":"劈分单个元素,前半部分作为键,后半部分作为值,放入一个Hashtable中:
key value
名称 Bill
职位 SSE
年龄 45
薪水 10000
解析部分代码如下:
/** *//**
* 将分段字符串链表转化成成员链表,不成功者记入错误链表
*
* @param segmentList
* 分段字符串链表
*/
private void changeSegmentToMember(List<String> segmentList) {
for (String segment : segmentList) {
Map<String, String> ht = StringUtil.parseStr2Map(segment, " ", ":");
Member member = new Member();
if (member.setHtToProperties(ht)) {
// 成功赋值,将成员放入成员列表
memberList.add(member);
} else {
// 有任何错误,将分段信息放入错误链表
errorList.add(segment);
}
}
}
赋值和验证部分:
然后把这个Hashtable传入到Member的一个函数setHtToProperties中,这个函数的任务是对Hashtable中的键值对进行遍历,在调用函数setValueToProperty对字段进行赋值:
代码如下:
/** *//**
* 将哈息表中成对的值按规则输入属性
* @param ht
* @return
*/
public boolean setHtToProperties(Map<String,String> ht){
Iterator it=ht.keySet().iterator();
while(it.hasNext()){
String key=(String)it.next();
String value=(String)ht.get(key);
boolean isSettted=setValueToProperty(key,value);
if(isSettted==false){
return false;
}
}
return true;
}
/** *//**
* 在mapping关系中用属性名去找属性对应的变量,是则赋值;如找不到或转化出错则返回假
* @param propertyName 属性名,如name对应的名称
* @param propertyNalue 属性值,如那么对应的Bill
* @return
*/
private boolean setValueToProperty(String propertyName,String propertyNalue){
if(propertyName.equals("名称")){
name=propertyNalue;
}
else if(propertyName.equals("年龄")){
try{
int ageTemp=Integer.parseInt(propertyNalue);
age=ageTemp;
}
catch(Exception e){
return false;
}
}
else if(propertyName.equals("职位")){
title=propertyNalue;
}
else if(propertyName.equals("薪水")){
try{
int salaryTemp=Integer.parseInt(propertyNalue);
salary=salaryTemp;
}
catch(Exception e){
return false;
}
}
else{
return false;
}
return true;
}
建立setValueToProperty函数的初衷是,用分支语句建立起键值与字段的对应关系,对应上了则进行赋值,这和Mapping有点类似,有些转化和验证工作也在分支内进行,只要验证出现问题即退出处理.
这样的处理方法带来了如下好处:
1.外界的类只需要解析文本,不需也不应该知道如何向Member的对应字段赋值,这个工作应该由Member自己进行,setHtToProperties函数帮助达成了这一点,有效降低了Member和其它类的耦合程度.
2.即使职位薪水年龄三个字段和值缺失,也不影响其它字段的赋值过程.
3.如果增加字段,setValueToProperty函数中只需要增加一个Mapping分支即可,其它地方无须改动.
4.对数据的校验工作可以统一在setValueToProperty函数中完成.
进行了如此处理后,代码量也不见得比混合处理多多少,而程序更加清晰,适应性也增强了,经得起不断更改. 比解析验证赋值混合在一起的方案要强的多.
完整代码下载:
http://www.blogjava.net/Files/sitinspring/MemberProcessor20071207163615.rar
面试题中常有HashMap和Hashtable的异同比较题,今天闲着无事,做了一点小比较,实验结果如下:
|
HashMap |
Hashtable |
允许空键 |
允许 |
不允许 |
允许空值 |
允许 |
不允许 |
以空键取值 |
能取到值 |
|
取空值 |
能取得 |
|
插值速度 |
稍高 |
稍低 |
取值速度 |
高 |
低 |
遍历速度 |
两者差不多 |
两者差不多
|
测试代码如下:
package com.sitinspring;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
public class Main {
public static void main(String[] args) {
Map<String, String> hashMap = new HashMap<String, String>();
Map<String, String> hashTable = new Hashtable<String, String>();
// 测试一:往两者中放空键,hashMap允许,hashTable不允许,第二句会抛出空指针异常
hashMap.put(null, "value");
hashTable.put(null, "value");
// 测试二:往两者中放空值,hashMap允许,hashTable不允许,第二句也会抛出空指针异常
hashMap.put("key", null);
hashTable.put("key", null);
// 测试三:以空键取hashMap中的值,结果能够取到"value"
String value = hashMap.get(null);
System.out.println("取出的值等于" + value);
// 测试四:以键"key"取hashMap中的值,结果能够取到null
String value2 = hashMap.get("key");
System.out.println("取出的值等于" + value2);
// 测试五:插值速度比较,两者差别不大
int max=100000;
TimeTest tableTimeTest=new TimeTest();
setValuesToMap(hashTable,max);
tableTimeTest.end("往hashTable插"+max+"值耗时");
hashMap.clear();// 清楚掉原来加入的值
TimeTest mapTimeTest=new TimeTest();
setValuesToMap(hashMap,max);
mapTimeTest.end("往hashMap插"+max+"个值耗时");
// 测试六:取值速度比较,hashTable速度平均约为hashMap的四分之一到七分之一
TimeTest tableTimeTest2=new TimeTest();
getValuesFromMap(hashTable,max);
tableTimeTest2.end("从hashTable取"+max+"值耗时");
TimeTest mapTimeTest2=new TimeTest();
getValuesFromMap(hashMap,max);
mapTimeTest2.end("往hashMap取"+max+"个值耗时");
// 测试七:遍历速度比较,hashTable速度和hashMap的差不多
TimeTest tableTimeTest3=new TimeTest();
traversalMap(hashTable);
tableTimeTest3.end("遍历hashTable耗时");
TimeTest mapTimeTest3=new TimeTest();
traversalMap(hashMap);
mapTimeTest3.end("遍历hashMap耗时");
}
private static void setValuesToMap(Map<String,String> map,int max){
for(int i=0;i<max;i++){
String str=String.valueOf(i);
map.put(str, str);
}
}
private static void getValuesFromMap(Map<String,String> map,int max){
for(int i=0;i<max;i++){
String str=map.get(i);
}
}
private static void traversalMap(Map<String,String> map){
Iterator it=map.keySet().iterator();
while(it.hasNext()){
String key=(String)it.next();
String value=map.get(key);
}
}
}
package com.sitinspring;
import java.util.Calendar;
import java.util.GregorianCalendar;
public class TimeTest {
private Calendar startTime;
public TimeTest() {
startTime = new GregorianCalendar();
}
public void end(String functionName) {
Calendar endTime = new GregorianCalendar();
int miniteSpan = endTime.get(Calendar.MINUTE)
- startTime.get(Calendar.MINUTE);
int secondSpan = endTime.get(Calendar.SECOND)
- startTime.get(Calendar.SECOND);
int msecondSpan = endTime.get(Calendar.MILLISECOND)
- startTime.get(Calendar.MILLISECOND);
System.out.println(functionName + " " + String.valueOf(miniteSpan)
+ " 分 " + String.valueOf(secondSpan) + " 秒 "
+ String.valueOf(msecondSpan) + " 毫秒 ");
}
}
代码下载:
http://www.blogjava.net/Files/sitinspring/HashMapHashtable20071215212107.rar
摘要: 字符串处理是许多程序中非常重要的一部分,它们可以用于文本显示,数据表示,查找键和很多目的.在Unix下,用户可以使用正则表达式的强健功能实现这些目的,从Java1.4起,Java核心API就引入了java.util.regex程序包,它是一种有价值的基础工具,可以用于很多类型的文本处理,如匹配,搜索,提取和分析结构化内容.
java.util.regex是一个用正则表达式所订制的模式来对字符...
阅读全文