2009年2月13日
IoC : Inversion of Control
spring是通过依赖注入(Dependency Injection )实现的IoC
IoC容器
* 必须将被管理的对象定义到spring配置文件中
* 必须定义constructor或者setter方法,让spring将对象注入进去
AOP : Aspect Oriented Programming
spring带来了一种编程方式,面向切面的编程。
AOP是一个概念
在一个程序中分离一个功能,这种功能的实现是与程序不相关的类。
同时能够使很多类共享这个功能。
关注是他的主要点,要关注某个功能,要关注切入点。
实现AOP有2点:
1、Pointcut(切入点)是一个范围---表达式
2、Advice(具体实现)功能放到那个方法
spring对hibernate的支持
注入SessionFactory到spring配置文件中
<Bean id="SessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactory">
<property nema="configLocation">
<value>classpath:hibernate.cfg.xml</value>
</property>
</Bean>
2009年1月20日
在库中建表时语句为: create table tbname(..........)engine=MyISAM character set gbk collate gbk_chinese_ci;
程序中连接数据库的Connection对象需要写成Connection con = DriverManaager.getConnection("jdbc:mysql://...user=..&password=...&useUnicode=true&characterEncoding=gbk");
若在终端下用mysql命令向数据库插入数据,则在进入mysql时的命令写成:#mysql --default-character-set=gbk -u ... -p
在网上看过很多解决mysql乱码的帖子,可是写的不是罗里罗嗦就过于复杂。其实乱码没那么复杂,是网上的大侠们想多了。我研究过一段日子,总结出一套自己的解决方案,如果你还没解决乱码就用我的方法试一下。我的方案不是最好的,却是最简单易懂的。不信你试试。
在使用MYSQL时,插入中文字符,经常会出现乱码,中文全被用?代替。出现这种情况的原因,多是字符集不匹配造成的。
在MYSQL中,如果使用缺省的字符集,在建库、建表时,默认使用的是latin1字符集,为ISO 8859-1西欧字符集。插入中文字符时,与之不匹配,就会出现乱码。
要解决此问题,就必须手动将数据库Server和Client的字符编码改为gb2312。配置方法如下:
打开MYSQL安装目录下的my.ini文件,找到如下段落:
# CLIENT SECTION
# ----------------------------------------------------------------------
#
# The following options will be read by MySQL client applications.
# Note that only client applications shipped by MySQL are guaranteed
# to read this section. If you want your own MySQL client program to
# honor these values, you need to specify it as an option during the
# MySQL client library initialization.
#
[client]
port=3306
[mysql]
default-character-set=latin1
# SERVER SECTION
# ----------------------------------------------------------------------
#
# The following options will be read by the MySQL Server. Make sure that
# you have installed the server correctly (see above) so it reads this
# file.
#
[mysqld]
# The TCP/IP Port the MySQL Server will listen on
port=3306
#Path to installation directory. All paths are usually resolved relative to this.
basedir="D:/MySQL/MySQL Server 5.0/"
#Path to the database root
datadir="D:/MySQL/MySQL Server 5.0/Data/"
# The default character set that will be used when a new schema or table is
# created and no character set is defined
default-character-set=latin1
将其中的default-character-set=latin1改为default-character-set=gb2312( 两个都改),然后重启MYSQL(特别注意:以前建立的数据库要重建,因为以前存储的数据编码方式为ISO-8859-1),运行MySQL Command Line Client:
输入show variables like 'character_set_%';可以查看数据库的字符编码如下:
mysql> show variables like 'character_set_%';
+--------------------------+-----------------------------------------+
| Variable_name | Value |
+--------------------------+-----------------------------------------+
| character_set_client | gb2312 |
| character_set_connection | gb2312 |
| character_set_database | gb2312 |
| character_set_filesystem | binary |
| character_set_results | gb2312 |
| character_set_server | gb2312 |
| character_set_system | utf8 |
| character_sets_dir | D:\MySQL\MySQL Server 5.0\share\charsets|
+--------------------------+-----------------------------------------+
然后,在程序中将连接数据库的URL改为jdbc:mysql://localhost:3306/databasename?useUnicode=true&characterEncoding=gb2312 就可以了!(&;是代表xml中的&)
还需要注意到是:1.你的JSP页面一定别忘了加上编码方式<%@ page pageEncoding="GB2312"%>。
2.把以前写过滤方法如:getbytes(iso-8859-1)全去掉
最后记住,一定要重新导入或建立数据库!!!
2009年1月1日
马老师说过,方法的重写一定要去父类拷贝。
今天深深的明白了这个道理。
今天写了个struts练习小项目。
写个控制器的类要继承Action类复写execute()
public ActionForward execute(
ActionMapping mapping,
ActionForm form,
HttpServletRequest request
HttpServletResponse response) throws Exception {
return null;
}
由于我是手写的,结果形参的顺序没写对。
恶心了我一下午。
还有异常的方法也要一样。
2008年12月31日
单态定义:
Singleton模式主要作用是保证在Java应用程序中,一个类Class只有一个实例存在。
Singleton模式就为我们提供了这样实现的可能。使用Singleton的好处还在于可以节省内存,因为它限制了
实例的个数,有利于Java垃圾回收(garbage collection)。
使用Singleton注意事项:
有时在某些情况下,使用Singleton并不能达到Singleton的目的,如有多个Singleton对象同时被不同的类
装入器装载;在EJB这样的分布式系统中使用也要注意这种情况,因为EJB是跨服务器,跨JVM的
单态模式的演化:
单态模式是个简单的模式,但是这个简单的模式也有很多复杂的东西。
一,首先最简单的单态模式,单态模式1
import java.util.*;
class Singleton
{
private static Singleton instance;
private Vector v;
private boolean inUse;
private Singleton()
{
v = new Vector();
v.addElement(new Object());
inUse = true;
}
public static Singleton getInstance()
{
if (instance == null) //1
instance = new Singleton(); //2
return instance; //3
}
}
这个单态模式是不安全的,为什么说呢 ?因为没考虑多线程,如下情况
Thread 1 调用getInstance() 方法,并且判断instance是null,然後进入if模块,
在实例化instance之前,
Thread 2抢占了Thread 1的cpu
Thread 2 调用getInstance() 方法,并且判断instance是null,然後进入if模块,
Thread 2 实例化instance 完成,返回
Thread 1 再次实例化instance
这个单态已经不在是单态
二,为了解决刚才的问题:单态模式2
public static synchronized Singleton getInstance()
{
if (instance == null) //1
instance = new Singleton(); //2
return instance; //3
}
采用同步来解决,这种方式解决了问题,但是仔细分析
正常的情况下只有第一次时候,进入对象的实例化,须要同步,
其它时候都是直接返回已经实例化好的instance不须要同步,
大家都知到在一个多线程的程序中,如果同步的消耗是很大的,很容易造成瓶颈
三,为了解决上边的问题:单态模式3,加入同步
public static Singleton getInstance()
{
if (instance == null)
{
synchronized(Singleton.class) {
instance = new Singleton();
}
}
return instance;
}
同步改成块同步,而不使用函数同步,但是仔细分析,
又回到了模式一的状态,再多线程的时候根本没有解决问题
四,为了对应上边的问题:单态模式4,也就是很多人采用的Double-checked locking
public static Singleton getInstance()
{
if (instance == null)
{
synchronized(Singleton.class) { //1
if (instance == null) //2
instance = new Singleton(); //3
}
}
return instance;
}
这样,模式一中提到的问题解决了。不会出现多次实例化的现象
当第一次进入的时候,保正实例化时候的单态,在实例化后,多线程访问的时候直接返回,不须要进入同步模块,
既实现了单态,又没有损失性能。表面上看我们的问题解决了,但是再仔细分析:
我们来假象这中情况:
Thread 1 :进入到//3位置,执行new Singleton(),但是在构造函数刚刚开始的时候被Thread2抢占cpu
Thread 2 :进入getInstance(),判断instance不等于null,返回instance,
(instance已经被new,已经分配了内存空间,但是没有初始化数据)
Thread 2 :利用返回的instance做某些操做,失败或者异常
Thread 1 :取得cpu初始化完成
过程中可能有多个线程取到了没有完成的实例,并用这个实例作出某些操做。
-----------------------------------------
出现以上的问题是因为
mem = allocate(); //分配内存
instance = mem; //标记instance非空
//未执行构造函数,thread 2从这里进入
ctorSingleton(instance); //执行构造函数
//返回instance
------------------------------------------
五,证明上边的假想是可能发生的,字节码是用来分析问题的最好的工具,可以利用它来分析
下边一段程序:(为了分析方便,所以渐少了内容)
字节码的使用方法见这里,利用字节码分析问题
class Singleton
{
private static Singleton instance;
private boolean inUse;
private int val;
private Singleton()
{
inUse = true;
val = 5;
}
public static Singleton getInstance()
{
if (instance == null)
instance = new Singleton();
return instance;
}
}
得到的字节码
;asm code generated for getInstance
054D20B0 mov eax,[049388C8] ;load instance ref
054D20B5 test eax,eax ;test for null
054D20B7 jne 054D20D7
054D20B9 mov eax,14C0988h
054D20BE call 503EF8F0 ;allocate memory
054D20C3 mov [049388C8],eax ;store pointer in
;instance ref. instance
;non-null and ctor
;has not run
054D20C8 mov ecx,dword ptr [eax]
054D20CA mov dword ptr [ecx],1 ;inline ctor - inUse=true;
054D20D0 mov dword ptr [ecx+4],5 ;inline ctor - val=5;
054D20D7 mov ebx,dword ptr ds:[49388C8h]
054D20DD jmp 054D20B0
上边的字节码证明,猜想是有可能实现的
六:好了,上边证明Double-checked locking可能出现取出错误数据的情况,那么我们还是可以解决的
public static Singleton getInstance()
{
if (instance == null)
{
synchronized(Singleton.class) { //1
Singleton inst = instance; //2
if (inst == null)
{
synchronized(Singleton.class) { //3
inst = new Singleton(); //4
}
instance = inst; //5
}
}
}
return instance;
}
利用Double-checked locking 两次同步,中间变量,解决上边的问题。
(下边这段话我只能简单的理解,翻译过来不好,所以保留原文,list 7是上边的代码,list 8是下边的
The code in Listing 7 doesn't work because of the current definition of the memory model.
The Java Language Specification (JLS) demands that code within a synchronized block
not be moved out of a synchronized block. However, it does not say that
code not in a synchronized block cannot be moved into a synchronized block.
A JIT compiler would see an optimization opportunity here.
This optimization would remove the code at
//4 and the code at //5, combine it and generate the code shown in Listing 8:)
-------------------------------------------------
list 8
public static Singleton getInstance()
{
if (instance == null)
{
synchronized(Singleton.class) { //1
Singleton inst = instance; //2
if (inst == null)
{
synchronized(Singleton.class) { //3
//inst = new Singleton(); //4
instance = new Singleton();
}
//instance = inst; //5
}
}
}
return instance;
}
If this optimization takes place, you have the same out-of-order write problem we discussed earlier.
如果这个优化发生,将再次发生上边提到的问题,取得没有实例化完成的数据。
-------------------------------------------------
以下部分为了避免我翻译错误误导打家,保留原文
Another idea is to use the keyword volatile for the variables inst and instance.
According to the JLS (see Resources), variables declared volatile are supposed to
be sequentially consistent, and therefore, not reordered.
But two problems occur with trying to use volatile to fix the problem with
double-checked locking:
The problem here is not with sequential consistency.
Code is being moved, not reordered.
Many JVMs do not implement volatile correctly regarding sequential consistency anyway.
The second point is worth expanding upon. Consider the code in Listing 9:
Listing 9. Sequential consistency with volatile
class test
{
private volatile boolean stop = false;
private volatile int num = 0;
public void foo()
{
num = 100; //This can happen second
stop = true; //This can happen first
//...
}
public void bar()
{
if (stop)
num += num; //num can == 0!
}
//...
}
According to the JLS, because stop and num are declared volatile,
they should be sequentially consistent. This means that if stop is ever true,
num must have been set to 100.
However, because many JVMs do not implement the sequential consistency feature of volatile,
you cannot count on this behavior.
Therefore, if thread 1 called foo and thread 2 called bar concurrently,
thread 1 might set stop to true before num is set to 100.
This could lead thread 2 to see stop as true, but num still set to 0.
There are additional problems with volatile and the atomicity of 64-bit variables,
but this is beyond the scope of this article.
See Resources for more information on this topic.
简单的理解上边这段话,使用volatile有可能能解决问题,volatile被定义用来保正一致性,但是很多虚拟机
并没有很好的实现volatile,所以使用它也会存在问题。
最终的解决方案:
(1),单态模式2,使用同步方法
(2),放弃同步,使用一个静态变量,如下
class Singleton
{
private Vector v;
private boolean inUse;
private static Singleton instance = new Singleton();
private Singleton()
{
v = new Vector();
inUse = true;
//...
}
public static Singleton getInstance()
{
return instance;
}
}
但使用静态变量也会存在问题,问题见 这篇文章
而且如在文章开头提到的,使用EJB跨服务器,跨JVM的情况下,单态更是问题
好了是不是感觉单态模式根本没法使用了,其实上边都是特殊情况,这中特殊情况的出现是有条件的,只要
根据你的具体应用,回避一些,就能解决问题,所以单态还是可以使用的。但是在使用前慎重,自己考虑好自己
的情况适合哪种情况。
2008年12月30日
2种实现线程的方法:1.继承Thread类。 2.实现Runnable接口。
多个线程操作同一资源的问题(不同步):第一个线程正操作资源,还没完成,第二个线程也来操作。
出现了资源错位。
解决的方法是资源的同步(synchronized)。即:一个线程访问资源的时候,别的线程不能访问该资源。
实现同步有2种方法: 1.把方法同步。 2.同步代码块。
实现了同步特征:1.线程安全了。 2.性能差了。(异步正好相反)
Object中的wait(),使线程进入等待状态,需要别的线程notify()继续工作。
wait()必须要在同步的情况下才能使用。
随之带来了死锁的问题。
面试题:
一、wait()和sleep()区别?
wait方法是Object类中的方法。
sleep方法是Thread类中的方法。
当线程进入wait时别的线程可以访问锁定的对象。
当线程进入sleep时别的线程不能访问锁定的对象。
二、写一个死锁。
class Demo implements Runnable {
int flag ;
static Object o1 = new Object(), o2 = new Object();
public void run(){
System.out.println("线程" + flag + "在运行。。");
if(flag == 1){
synchronized(o1){
try {
Thread.sleep(100);
} catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程1 --> o1");
synchronized(o2){
System.out.println("线程1 --> o2");
}
}
}
if(flag == 2){
synchronized(o2){
try {
Thread.sleep(100);
} catch(InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程2 --> o2");
synchronized(o1){
System.out.println("线程2 --> o1");
}
}
}
}
}
结果导致死锁。
线程1在运行。。
线程2在运行。。
线程1 --> o1
线程2 --> o2
不能继续进行。
2个Object类为static,说明他们锁定的是同2个对象;
三、Producer和Consumer的问题
涉及了wait方法和nofity方法
public class ProducerConsumer {
public static void main(String[] args) {
SyncStack ss = new SyncStack();
Producer p = new Producer(ss);
Consumer c = new Consumer(ss);
new Thread(p).start();
new Thread(c).start();
}
}
class WoTou {
int id;
WoTou(int id) {
this.id = id;
}
public String toString() {
return "WoTou : " + id;
}
}
class SyncStack {
int index = 0;
WoTou[] arrWT = new WoTou[6];
public synchronized void push(WoTou wt) {
while(index == arrWT.length) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.notifyAll();
arrWT[index] = wt;
index ++;
}
public synchronized WoTou pop() {
while(index == 0) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.notifyAll();
index--;
return arrWT[index];
}
}
class Producer implements Runnable {
SyncStack ss = null;
Producer(SyncStack ss) {
this.ss = ss;
}
public void run() {
for(int i=0; i<20; i++) {
WoTou wt = new WoTou(i);
ss.push(wt);
System.out.println("生产了:" + wt);
try {
Thread.sleep((int)(Math.random() * 200));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Consumer implements Runnable {
SyncStack ss = null;
Consumer(SyncStack ss) {
this.ss = ss;
}
public void run() {
for(int i=0; i<20; i++) {
WoTou wt = ss.pop();
System.out.println("消费了: " + wt);
try {
Thread.sleep((int)(Math.random() * 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
2008年12月7日
初学 Java 有段时间了,感觉似乎开始入了门,有了点儿感觉
但是发现很多困惑和疑问而且均来自于最基础的知识
折腾了一阵子又查了查书,终于对 String 这个特殊的对象有了点感悟
大家先来看看一段奇怪的程序:
public class TestString {
public static void main(String[] args) {
String s1 = "Monday";
String s2 = "Monday";
}
}
这个程序真是简单啊!可是有什么问题呢?
1. 来自 String 的忧虑
上面这段程序中,到底有几个对象呢?
可能很多人脱口而出:两个,s1 和 s2
为什么?
String 是 final 类,它的值不可变。
看起来似乎很有道理,那么来检测一下吧,稍微改动一下程序
就可以看到结果了:
public class TestString {
public static void main(String[] args) {
String s1 = "Monday";
String s2 = "Monday";
if (s1 == s2)
System.out.println("s1 == s2");
else
System.out.println("s1 != s2");
}
} 呵呵,很多人都会说已经不止两个对象了
编译并运行程序,输出:s1 == s2
啊!
为什么 s1 == s2 ?
== 分明是在说:s1 与 s2 引用同一个 String 对象 -- "Monday"!
2. 千变万化的 String 再稍微改动一下程序,会有更奇怪的发现:
public class TestString {
public static void main(String[] args) {
String s1 = "Monday";
String s2 = new String("Monday");
if (s1 == s2)
System.out.println("s1 == s2");
else
System.out.println("s1 != s2");
if (s1.equals(s2))
System.out.println("s1 equals s2");
else
System.out.println("s1 not equals s2");
}
} 我们将 s2 用 new 操作符创建
程序输出:
s1 != s2
s1 equals s2
嗯,很明显嘛
s1 s2分别引用了两个"Monday"String对象
可是为什么两段程序不一样呢?
3. 在 String 的游泳池中游泳
哈哈,翻了翻书终于找到了答案:
原来,程序在运行的时候会创建一个字符串缓冲池
当使用 s2 = "Monday" 这样的表达是创建字符串的时候,程序首先会
在这个String缓冲池中寻找相同值的对象,在第一个程序中,s1先被
放到了池中,所以在s2被创建的时候,程序找到了具有相同值的 s1
将 s2 引用 s1 所引用的对象"Monday"
第二段程序中,使用了 new 操作符,他明白的告诉程序:
“我要一个新的!不要旧的!”与是一个新的"Monday"Sting对象被创
建在内存中。他们的值相同,但是位置不同,一个在池中游泳
一个在岸边休息。哎呀,真是资源浪费,明明是一样的非要分开做什么呢?
4. 继续潜水
再次更改程序:
public class TestString {
public static void main(String[] args) {
String s1 = "Monday";
String s2 = new String("Monday");
s2 = s2.intern();
if (s1 == s2)
System.out.println("s1 == s2");
else
System.out.println("s1 != s2");
if (s1.equals(s2))
System.out.println("s1 equals s2");
else
System.out.println("s1 not equals s2");
}
} 这次加入:s2 = s2.intern();
哇!程序输出:
s1 == s2
s1 equals s2
原来,程序新建了 s2 之后,又用intern()把他打翻在了池里
哈哈,这次 s2 和 s1 有引用了同样的对象了
我们成功的减少了内存的占用
5. == 与 equals() 的争斗
String 是个对象,要对比两个不同的String对象的值是否相同
明显的要用到 equals() 这个方法
可是如果程序里面有那么多的String对象,有那么多次的要用到 equals ,
哦,天哪,真慢啊
更好的办法:
把所有的String都intern()到缓冲池去吧
最好在用到new的时候就进行这个操作
String s2 = new String("Monday").intern();
嗯,大家都在水池里泡着了吗?哈哈
现在我可以无所顾忌的用 == 来比较 String 对象的值了
真是爽啊,又快又方便!
看看 String 这一次又怎么闹事儿吧
1. 回顾一下坏脾气的 String 老弟
例程1:
class Str {
public static void main(String[] args) {
String s = "Hi!";
String t = "Hi!";
if (s == t)
System.out.println("equals");
else
System.out.println("not equals");
}
} 程序输出什么呢?
程序输出:equals
2. 哦,天哪,它又在搅混水了
例程2:
class Str {
public static void main(String[] args) {
String s = "HELLO";
String t = s.toUpperCase();
if (s == t)
System.out.println("equals");
else
System.out.println("not equals");
}
} 那么这个程序有输出什么呢?
慎重!再慎重!不要被 String 这个迷乱的家伙所迷惑!
它输出:equals
WHY!!!
把程序简单的更改一下:
class Str2 {
public static void main(String[] args) {
String s = "Hello";
String t = s.toUpperCase();
if (s == t)
System.out.println("equals");
else
System.out.println("not equals");
}
} 你可能会说:不是一样吗?不!千真万确的,不一样!这一次输出:
not equals Oh MyGOD!!!
谁来教训一下这个 String 啊!
3. 你了解你的马吗?
“要驯服脱缰的野马,就要了解它的秉性”牛仔们说道。
你了解 String 吗?解读 String 的 API ,可以看到:toUpperCase() 和 toLowerCase() 方法返回一个新的String对象,它将原字符串表示字符串的大写或小写形势;但是要注意:如果原字符串本身就是大写形式或小写形式,那么返回原始对象。这就是为什么第二个程序中 s 和 t 纠缠不清的缘故对待这个淘气的、屡教不改的 String ,似乎没有更好的办法了让我们解剖它,看看它到底有什么结构吧:
(1) charAt(int n) 返回字符串内n位置的字符,第一个字符位置为0,最后一个字符的位置为length()-1,访问错误的位置会扔出一块大砖头:StringIndexOutOfBoundsException 真够大的
(2) concat(String str) 在原对象之后连接一个 str ,但是返回一个新的 String 对象
(3) EqualsIgnoreCase(String str) 忽略大小写的 equals 方法这个方法的实质是首先调用静态字符方法toUpperCase() 或者 toLowerCase() 将对比的两个字符转换,然后进行 == 运算
(4) trim() 返回一个新的对象,它将原对象的开头和结尾的空白字符切掉同样的,如果结果与原对象没有差别,则返回原对象
(5) toString() String 类也有 toString() 方法吗?真是一个有趣的问题,可是如果没有它,你的 String 对象说不定真的不能用在System.out.println() 里面啊小心,它返回对象自己String 类还有很多其他方法,掌握他们会带来很多方便也会有很多困惑,所以坚持原则,是最关键的
4. 我想买一匹更好的马来购买更驯服温和的 String 的小弟 StringBuffer 吧
这时候会有人反对:
它很好用,它效率很高,它怎么能够是小弟呢?
很简单,它的交互功能要比 String 少,如果你要编辑字符串它并不方便,你会对它失望但这不意味着它不强大public final class String implements Serializable, Comparable, CharSequencepublic final class StringBuffer implements Serializable, CharSequence很明显的,小弟少了一些东东,不过这不会干扰它的前途StringBuffer 不是由 String 继承来的不过要注意兄弟它也是 final 啊,本是同根生看看他的方法吧,这么多稳定可靠的方法,用起来比顽皮的 String 要有效率的多? Java 为需要改变的字符串对象提供了独立的 StringBuffer 类它的实例不可变(final),之所以要把他们分开是因为,字符串的修改要求系统的开销量增大,占用更多的空间也更复杂,相信当有10000人挤在一个狭小的游泳池里游泳而岸边又有10000人等待进入游泳池而焦急上火又有10000人在旁边看热闹的时候,你这个 String 游泳池的管理员也会焦头烂额在你无需改变字符串的情况下,简单的 String 类就足够你使唤的了,而当要频繁的更改字符串的内容的时候,就要借助于宰相肚里能撑船的StringBuffer 了
5. 宰相肚里能撑船
(1) length() 与 capacity()String 中的 length() 返回字符串的长度兄弟 StringBuffer 也是如此,他们都由对象包含的字符长度决定capacity()呢?
public class TestCapacity {
public static void main(String[] args){
StringBuffer buf = new StringBuffer("it was the age of wisdom,");
System.out.println("buf = " + buf);
System.out.println("buf.length() = " + buf.length());
System.out.println("buf.capacity() = " + buf.capacity());
String str = buf.toString();
System.out.println("str = " + str);
System.out.println("str.length() = " + str.length());
buf.append(" " + str.substring(0,18)).append("foolishness,");
System.out.println("buf = " + buf);
System.out.println("buf.length() = " + buf.length());
System.out.println("buf.capacity() = " + buf.capacity());
System.out.println("str = " + str);
}
}
程序输出:
buf = it was the age of wisdom.buf.length() = 25
buf.capacity() = 41
str = it was the age of wisdomstr.length() = 25
buf = it was the age of wisdom, it was the age of foolishness,
buf.length() = 56
buf.capacity() = 84
str = it was the age of wisdom,
可以看到,在内容更改之后,capacity也随之改变了长度随着向字符串添加字符而增加而容量只是在新的长度超过了现在的容量之后才增加StringBuffer 的容量在操作系统需要的时候是自动改变的程序员们对capacity所能够做的仅仅是可以在初始化 StringBuffer对象的时候。
转载自:http://www.leadbbs.com/a/a.asp?B=222&ID=1419085
为了让equals方法来判断2个对象的内容是否相同。
重写了equals方法。
class Test2 {
public static void main(String args[]) {
Dog d1 = new Dog(1);
Dog d2 = new Dog(1);
Dog d3 = new Dog(2);
System.out.println(d1 == d2);//false
System.out.println(d1.equals(d2));//true
System.out.println(d1.equals(d3));//false
}
}
class Dog {
private int i;
Dog(int i) {
this.i = i;
}
public boolean equals(Object o) {
if(o != null) {
if(o instanceof Dog) { //判断传过来的 对象o 是否属于Dog类型的
Dog d = (Dog)o; //把对象o强制转换成Dog对象类型
if(this.i == d.i) {
return true;
}
}
}
return false;
}
}
2008年12月6日
我有很多概念都没明白,现在反过头来认识。
Object是所有类的父类。
java.lang.String也是继承自java.lang.Object中。
先比较一下2个类的toString()、equals()、= = (很多面试题都有),再来说一下String 的特殊。
2个类toString()的比较:
class Test {
public static void main(String [] args) {
Object obj = new Object();
Dog d = new Dog();
String s = new String("abc");
System.out.println("obj = " + obj);
System.out.println("d = " + d);
System.out.println("s = " + s);
}
}
class Dog {}
结果:
obj = java.lang.Object@c17164
d = Dog@1fb8ee3
s = abc
查阅API文档:
Object类的 public String toString() returns
getClass().getName() + '@' + Integer.toHexString(hashCode())
String类的 public String toString() returns
the string itself.
说明String类重写了Object的toString方法。
2个类equals()、= =比较:
class Test {
public static void main(String [] args) {
//比较equals()和==
Dog d1 = new Dog(1);
Dog d2 = new Dog(1);
System.out.println("d1 == d2 " + (d1 == d2)); //false
System.out.println("d1.equals(d2) " + d1.equals(d2)); //false
String s1 = new String("abc");
String s2 = new String("abc");
String s3 = "abc";
System.out.println("s1 == s2 " + (s1 == s2)); //false
System.out.println("s1.equals(s2) " + s1.equals(s2)); //ture
System.out.println("s1 == s3 " + (s1 == s3)); //false
System.out.println("s1.equals(s3) " + s1.equals(s3)); //true
}
}
class Dog {
private int i;
Dog(){}
Dog(int i){
this.i = i;
}
}
"=="是比较2个对象的引用是否指向同一个地址,如果是比较2个基本类型,那么就是比较2个值是否相等。
我new了2个Dog类,他们2个对象的栈引用肯定不能指向同一个堆地址。第二个String对象"=="的比较也是同样道理。所以都返回了false(第三个比较体现了String的特殊类,等下说明)。
查阅jdk的源代码:"equals"在Object中定义为
public boolean equals(Object obj) {
return (this == obj);
}
这就表示一个类如果没有重写该方法的话,它就是比较两个的引用是否指向了同一个地址(和"=="比较是一致的)。
类库里的类基本都重写的该方法,所以是比较2个对象的内容是否一样的(String方法重写了该方法,所以第二个equals比较返回ture),对于外面自己自定义的类,则需要自己重写该方法来达到内容是否相等的逻辑。
特殊对象String的equals()、= =比较:
String s = newString("abc");和String s = "abc";
首先2个对象在内存分布上是不一样的。第2个对象是s的栈引用指向数据片段区的地址(存放字符串常量,静态成员变量)。第1个new出来的对象是s的栈引用指向堆中的地址(存放在堆中)。通过API文档:新创建的字符串对象s是该参数字符串("abc")的副本。
所以如代码所表示:"=="比较是不一样的,equals方法由于String重写了,比较是字符序列。
String s1 = "abc" 和 String s2 = "abc"比较;
"=="和equals方法都返回ture;
s1分配了字符常量"abc",那么再有"abc"要引用给对象,都不会再分配内存空间了。
所以s1 和s2 指向的空间相同,内容也相同。
刚来的,先试一下手。
以后再慢慢搬。
1.选择排序
class NumSort
{
public static void main(String[] args)
{
int [] a = new int[args.length];
for(int i=0; i<a.length ; i++)
{
a[i] = Integer.parseInt(args[i]);
}
print(a);
selectSort(a);
print(a);
}
private static void selectSort(int [] a)
{
for(int i=0; i<a.length; i++)
{
for(int j=i+1; j<a.length; j++)
{
if(a[i] > a[j])
{
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
}
}
private static void print(int [] a)
{
for(int i=0; i<a.length ; i++)
{
System.out.print(a[i] + " ");
}
System.out.println();
}
}
2.冒泡排序