String
==与equals
看了一部分讲解,个人理解,应该时刻记得String是一个类,并不是int等基本类型。由于类是用new在堆上创建对象,且对象的地址存在于栈中,而int等基本类型是在栈上创建。所以用“==”比较的是栈内的值,也就是说用“==”比较基本类型时,比较的是基本类型的取值,而用“==”比较类的对象时,比较的是该对象的地址。当使用“equals”比较时,比较的是堆中的取值是否相同。因此String等类在比较对象的取值时应该用equals。
实时读写文件
写properties
不使用Spring 的 Environment
有一次在编码的时候,使用的是Spring 的Environment类,导致无法实时读取,可能是由于Environment类只是在程序启动的时候将文件读入内存,之后不再反复读取的原因。只要正常使用Properties类,在每次读取时重新打开输入流,读取之后关闭输入流即可做到实时读取。
SafeProperties properties = new SafeProperties();
try {
InputStream inputStream = new BufferedInputStream(new FileInputStream(propertiesFilePath));
properties.load(inputStream);
inputStream.close();
} catch (Exception exception) {
exception.printStackTrace();
}
只读写内存中的,不写入实体文件
参考http://bbs.youkuaiyun.com/topics/392066071,使用如下方式:
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("BaseConfig.properties");
OutputStream outputStream = new FileOutputStream(new File(this.getClass().getClassLoader().getResource("BaseConfig.properties").getFile()));
此时加载的是class编译后的执行路径,可以做到写完数据后,下次读的时候读取到的是新数据。但从始至终,即使程序退出,也不会将数据写入到实际的文件中。例子:
public class ConfigUtil {
private static ConfigUtil instance;
private static Properties properties = null;
private ConfigUtil() {
properties = new Properties();
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("BaseConfig.properties");
try {
properties.load(inputStream);
} catch (IOException ex) {
Logger.getLogger(ConfigUtil.class.getName()).log(Level.SEVERE, null, ex);
}finally{
if (inputStream!=null) {
try {
inputStream.close();
} catch (IOException ex) {
Logger.getLogger(ConfigUtil.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
public static ConfigUtil getInstance() {
if (instance == null) {
instance = new ConfigUtil();
}
return instance;
}
public String getValueByKey(String key) {
return (String) properties.get(key) == null ? "空" : (String) properties.get(key);
}
public void setValueByKey(String key ,String value) throws FileNotFoundException, IOException {
properties.setProperty(key, value);
OutputStream outputStream = new FileOutputStream(new File(this.getClass().getClassLoader().getResource("BaseConfig.properties").getFile()));
properties.store(outputStream, "");
outputStream.close();
}
public static void main(String[] a) throws FileNotFoundException, IOException {
ConfigUtil configTool = ConfigUtil.getInstance();
System.out.println(configTool.getValueByKey("test"));
configTool.setValueByKey("test", "33");
System.out.println(configTool.getValueByKey("test"));
configTool.setValueByKey("test", "44");
System.out.println(configTool.getValueByKey("test"));
configTool.setValueByKey("test", "55");
System.out.println(configTool.getValueByKey("test"));
}
}
使用线程读入,该方法没有做到实时
使用新的线程读入properties文件,参考http://java-my-life.iteye.com/blog/1313706
InputStream in = new BufferedInputStream(new FileInputStream(Thread
.currentThread().getContextClassLoader().getResource(
"test.properties").getPath()));
例子
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Properties;
public class Test extends Thread{
public void run()
{
while(true){
try {
this.sleep(1000);
readProperties();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private static void readProperties()
{
Properties p = new Properties();
try {
InputStream in = new BufferedInputStream(new FileInputStream(Thread
.currentThread().getContextClassLoader().getResource(
"test.properties").getPath()));
p.load(in);
in.close();
String name = p.getProperty("name");
String id = p.getProperty("id");
System.out.println("id=" + id + "\t name=" + name);
} catch (Exception e1) {
e1.printStackTrace();
}
}
public static void main(String[]args)
{
new Test().start();
}
}
读properties
防止乱码
为了防止乱码,load时可以使用InputStreamReader,然后设置charsetName为utf-8。但是使用这种方式写入时,无论是否使用SafeProperties等自定义的类,都无法保留properties文件中的注释及空行。所以尽量读写分离。
Properties properties = new SafeProperties();
InputStream inputStream = new BufferedInputStream(new FileInputStream(propertiesFilePath));
properties.load(new InputStreamReader(inputStream, "utf-8"));
inputStream.close();
读取JAR包内的文件
获取流
被读取的文档路径要设置成以JAR包根目录为基础的路径,如下图,environment.properties文件在JAR包根目录下的properties文件夹内,因此路径要写成”properties/environment.properties”
InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("properties/environment.properties");
properties.load(inputStream);
inputStream.close();
同步/互斥
Semaphore
模拟mutex
在定义Semaphore的时候,令可用资源数量=1,即可模拟mutex。
定时任务
ScheduledExecutorService
启动
使用ScheduledExecutorService的好处有很多,参考说明,相比于Timer具体有三处优势,1是支持多线程多任务,2是对于每个线程异常的单独处理,3是Timer执行周期任务时依赖系统时间,当系统时间变化时,会影响执行上的变化,ScheduledExecutorService基于时间的延迟,不会由于系统时间的改变发生执行变化。使用方法参考例子:
package com.effective.common.concurrent.execute;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class Schedule {
private static DateFormat dateFormat = new SimpleDateFormat("yy-MM-dd HH:mm:ss");
private static DateFormat dayFormat = new SimpleDateFormat("yy-MM-dd");
private static ScheduledExecutorService excutor = Executors.newSingleThreadScheduledExecutor();
/**
* 按指定频率周期执行某个任务 <br>
* 初始化延迟0ms开始执行,每隔5ms重新执行一次任务。
*/
public void fixedRate(){
excutor.scheduleAtFixedRate(new EchoServer(), //执行线程
0, //初始化延迟
5000, //两次开始的执行的最小时间间隔
TimeUnit.MILLISECONDS //计时单位
);
}
/**
*
*/
public void fixDelay(){
excutor.scheduleWithFixedDelay(new EchoServer(),//执行线程
0, //初始化延迟
5000, //前一次执行结束到下一次执行开始的间隔时间
TimeUnit.MILLISECONDS);
}
/**
* 每天晚上8点执行一次
*/
public void dayOfDelay(String time){
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
long oneDay = 24 * 60 * 60 * 1000;
long initDelay = getTimeMillis("20:00:00") - System.currentTimeMillis();
initDelay = initDelay > 0 ? initDelay : oneDay + initDelay;
executor.scheduleAtFixedRate(
new EchoServer(),
initDelay,
oneDay,
TimeUnit.MILLISECONDS);
}
/**
* 获取给定时间对应的毫秒数
* @param string "HH:mm:ss"
* @return
*/
private static long getTimeMillis(String time) {
try {
Date currentDate = dateFormat.parse(dayFormat.format(new Date()) + " " +time);
return currentDate.getTime() ;
} catch (ParseException e) {
e.printStackTrace();
}
return 0;
}
public static void main(String[] args){
Schedule schedule = new Schedule();
schedule.fixedRate();
schedule.fixDelay();
}
}
取消定时任务
使用下述语句可以取消已经建立的定时任务,但是我仅仅在线程未执行或执行结束的时候用过,执行shutdown后该线程不再定时启动。若该任务线程正在执行,不知道是否可以强行终止。
scheduledExecutorService.shutdown();
当使用newSingleThreadScheduledExecutor()创建时,可能是由于创建的是单独的一个线程,所以在线程创建之后,线程池中已经空了。在shutdown之后,当再次启动定时任务时,会由于线程池空了而报错,异常信息:
java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask@6386eba1 rejected from java.util.concurrent.ScheduledThreadPoolExecutor@5e5a567f[Terminated, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0]
因此在使用newSingleThreadScheduledExecutor()且执行了shutdown操作后,若是想再次启动定时任务,需要重新通过newSingleThreadScheduledExecutor()获取对象。
scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
Swing
多线程中不显示界面
下图为一个创建界面的类,当在绘制swing界面的类内部,直接在函数中使用sleep或semaphore的acquire函数时,会导致界面显示异常,仅仅剩下一个frame,无法显示加载的pannel。
因此若需要在多个线程所控制的界面之间切换,要在线程内的run函数中写sleep,acquire等函数,这样才能正常显示。至于导致这一问题的原因,还不清楚,需要再查资料看一看,怀疑是因为sleep等导致阻塞,使得pannel无法显示。
设置图标
设置程序显示图标
为JFrame添加图标,由于java只识别jpg、png等,不能识别ico,因此不能使用ico图片。
Image image = Toolkit.getDefaultToolkit().getImage(iconPath + "\\mainForm.png");
frame.setIconImage(image);
窗口按钮
不关闭窗口
当点击窗口右上角的红叉时,不关闭窗口,并执行重写的windowClosing操作。
frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
JOptionPane.showMessageDialog(null, "请等待数据处理结束!");
}
});