求java大牛,关于单例模式的使用问题,小白真心求教~

上帝爱众生 发布于 2015/04/16 22:30
阅读 617
收藏 0

今天用JDBC链接数据库进行了对数据库的操作,之前对“加载数据库驱动和连接”部分进行了初级封装,发现我只要使用该类得到Connection类的对象就会加载和连接一次数据库,效率较低,损耗较大,所以我就想让它一次性加载不再重复加载,所以我就用了单例(Singleton)模式对该类进行了封装,代码如下:

package sun.juwin.utils;


import java.sql.Connection;
import java.sql.DriverManager;
/*
 * 这个类使用单例模式编写
 * */
public class Conn {
private Connection con;//Connection类型的成员变量,最后在Dao层使用该类对象通过它的get方法得到它,通过它操作数据库
private static Conn connobj;//这是一个静态的本类的一个成员变量,用于调用con的get方法 
Conn(){//构造函数,用于初始化对象,建立数据库连接
try{
Class.forName("com.mysql.jdbc.Driver");//加载数据库驱动
String url="jdbc:mysql://localhost:3306/db2";//
this.con=DriverManager.getConnection(url,"root","123");//通过getConnection初始化con
}catch(Exception e){
e.printStackTrace();
}
}
public static Conn getConn(){//一个能得到Conn实例的一个方法,从这里开始启用单例模式
if (connobj == null) {//判断当前本类对象是否已经初始化
System.out.println("safafasf");//用来测试
connobj = new Conn(); //如果未被初始化则调用构造方法初始化
}
return connobj;//返回本类对象,至此单例结束
}
public Connection getCon() {
return con;
}
public void setCon(Connection con) {
this.con = con;
}
}

这是Dao层:

public int adduser(String uname){
int flag=0;
Connection con=Conn.getConn().getCon();//通过调用getConn得到connection对象
String sql="insert into user(uname)values(?)";
try{
ps=con.prepareStatement(sql);
ps.setString(1,uname);
flag=ps.executeUpdate();
}catch(SQLException e){
e.printStackTrace();
}
finally{
try{
if(rs!=null){
rs.close();
}if(ps!=null){
ps.close();
}
}catch(SQLException e){
e.printStackTrace();
}
}
return flag;
}

这是测试类:

public class Test {
public static void main(String[] args){
UserDao ud=new UserDao();
ud.adduser("ssssqqqqqqeewww2");//调用adduser添加用户
System.out.println("Success!");//用来做标记
}

}

按理说使用单例就是只对类初始化一次,然后一直使用这个对象进行调用类中方法即可,可是上面的代码最终测试结果:

第一次:

safafasf

Success!

很正常,因为第一次单例还没有初始化任何对象,所以会打印“safafasf

可是第二次结果居然是这样的:

safafasf

Success!

我很好奇,就是说每一次我都进行初始化了,这是怎么回事啊?小白求教啊!第一次使用单例,居然结果是这么结果

如果我的想法不对或者单例用的不对请指出来吧,拜托,先谢谢了~

加载中
0
yzChen233
yzChen233

姑且不看你的Conn类,就看你的main方法。

请问你是

ud.adduser("sxxxxxxxxxxxxxxwww2");
System.out.println("Success!");



还是
UserDao ud=new UserDao();
ud.adduser("ssssqqqqqqeewww2");

ud.adduser("ssxxxxxxxxxxxxxewww2");
System.out.println("Success!");



如果是前者,你每次都重新执行代码环境,当然每次都会new一次啊。

如果是后者,目测是没问题

上帝爱众生
上帝爱众生
回复 @倚楼听风雨_ : 对了,真是的,我这次调用了两次Dao层方法,果然只连接一次,可是有人说这样会内存溢出,怎么会这样?而且现在不能把Connection给Close掉(你懂的),这样有会有什么后果?
yzChen233
yzChen233
回复 @来自爪哇岛的程序猿 : 我说的是你的if connobj == null,在第一种代码下,是永远都是 true。
上帝爱众生
上帝爱众生
main方法里面肯定得先new一个UserDao对象ud才能调用adduser方法,adduser里面再去访问Conn啊,
0
迪伦少校
迪伦少校
不说别的,最基本的单例,构造函数也应该是 private的。。。
上帝爱众生
上帝爱众生
忘了写了o(╯□╰)o,可是写了也是一样的,每次都会执行。。。。。。
0
J
Jwxl

就我自己开发遇到的问题来看:

你这代码如果是在并发环境,是有并发安全问题的

上帝爱众生
上帝爱众生
并发环境?每次Dao层用到了Conn就直接用了啊,因为这时候堆里面肯定有了Conn对象,直接拿来用不就行了吗?怎么会并发呢?大神,求解释啊,因为网上很多人都说单例实现jdbc链接会并发。。。。。
0
刘柳
刘柳
用连接池吧,别折腾了
上帝爱众生
上帝爱众生
我知道可以用连接池,可是我不明白这样为什么不行啊?唉,郁闷。。。。
0
J
Jwxl

你需要 使用经典的双检查模式:

if(conn ==null){
 	synchronized (xxx.class) {
		if(conn ==null){
			conn = new Conn();
		}
	}
}



这么做还有个前提conn 必须是 volatile的, java1.6+以上版本才可以这么写
上帝爱众生
上帝爱众生
不行啊,还是执行了两次。。。。。
0
大杯白开水
大杯白开水
写连接池,可以把connection绑定到thread local,这样没并发问题了,也不用写单例加锁存在性能问题
上帝爱众生
上帝爱众生
刚刚听人说数据库连接如果用单例的话会造成内存溢出,为什么会这样?他说Dao层有很多方法都用到了数据库连接,所以会溢出,不懂啊,明明已经通过单例得到一个对象了为什么Dao层用的多还会影响它。。。。
0
yzChen233
yzChen233

引用来自“倚楼听风雨_”的评论

姑且不看你的Conn类,就看你的main方法。

请问你是

ud.adduser("sxxxxxxxxxxxxxxwww2");
System.out.println("Success!");



还是
UserDao ud=new UserDao();
ud.adduser("ssssqqqqqqeewww2");

ud.adduser("ssxxxxxxxxxxxxxewww2");
System.out.println("Success!");



如果是前者,你每次都重新执行代码环境,当然每次都会new一次啊。

如果是后者,目测是没问题

package test;


public class Conn {

private static Conn conn;

private Conn() {}

public static Conn getConn() {
if(null == conn) {
System.out.println("new Conn!");
conn = new Conn();
}
return conn;
}

public static void main(String[] args) {
Dao dao = new Dao();
 dao.addUser("u A");
 /*
 Dao daoA = new Dao();
 daoA.addUser("u A");
 Dao daoB = new Dao();
 daoB.addUser("u B");
 */
}


}




class Dao {

public void addUser(String uname) {
Conn conn = Conn.getConn();
System.out.println("add user --> " + uname);
}

}




自己拿去main测试去

0
vvtf
vvtf

我反正觉得是楼主的执行问题。

难道是main执行了一次,然后再进行?

还是一个循环执行的?

0
徐迎龙
徐迎龙

既然没有构造参数,那么就直接

private static Conn connobj=new Conn ();

不要在getConn的时候进行判断了,直接返回connobj变量

0
上帝爱众生
上帝爱众生
好吧,其实是对的,我在main里面多写了一句add方法,结果只连接了一次,说明单例奏效了。。。。。
返回顶部
顶部