java刚開始学习的人常见的问题
本文为那些准Java程序猿们准备了一系列广为流传的Java最佳编程实践:
1. 优先返回空集合而非null
假设程序要返回一个不包括不论什么值的集合,确保返回的是空集合而不是null。
这能节省大量的”if else”检查。
publicclass getLocationName {
return(null==cityName ?
"": cityName);
}
1. 慎重操作字符串
假设两个字符串在for循环中使用+操作符进行拼接,那么每次循环都会产生一个新的字符串对象。这不仅浪费内存空间同一时候还会影响性能。相似的,假设初始化字符串对象,尽量不要使用构造方法,而应该直接初始化。例如说:
//Slower Instantiation
String bad =newString("Yet another string object");
//FasterInstantiation
String good ="Yet another string object"
1. 避免无用对象
创建对象是Java中最昂贵的操作之中的一个。
因此最好在有须要的时候再进行对象的创建/初始化。例如以下:
import java.util.ArrayList;
import java.util.List;
publicclassEmployees{
privateListEmployees;
publicList getEmployees(){
//initialize only when required
if(null==Employees){
Employees=newArrayList();
}
returnEmployees;
}
}
1. 数组与ArrayList之争
开发者经常会发现非常难在数组和ArrayList间做选择。
它们二者互有优劣。
怎样选择应该视情况而定。
import java.util.ArrayList;
publicclass arrayVsArrayList {
publicstaticvoid main(String[] args){
int[] myArray =newint[6];
myArray[7]=10;//ArraysOutOfBoundException
//Declaration of ArrayList. Add and Remove of elements is easy.
ArrayList<Integer> myArrayList =newArrayList<>();
myArrayList.add(1);
myArrayList.add(2);
myArrayList.add(3);
myArrayList.add(4);
myArrayList.add(5);
myArrayList.remove(0);
for(int i =0; i < myArrayList.size(); i++){
System.out.println("Element:"+ myArrayList.get(i));
}
//Multi-dimensional Array
int[][][] multiArray =newint[3][3][3];
}
}
· 数组是定长的。而ArrayList是变长的。由于数组长度是固定的,因此在声明数组时就已经分配好内存了。
而数组的操作则会更快一些。
还有一方面,假设我们不知道数据的大小。那么过多的数据便会导致ArrayOutOfBoundException,而少了又会浪费存储空间。
· ArrayList在增删元素方面要比数组简单。
· 数组能够是多维的,但ArrayList仅仅能是一维的。
1. try块的finally块没有被运行
看下以下这段代码:
publicclass shutDownHooksDemo {
publicstaticvoid main(String[] args){
for(int i=0;i<5;i++)
{
try{
if(i==4){
System.out.println("Inside Try Block.Exiting withoutexecuting Finally block.");
System.exit(0);
}
}
finally{
System.out.println("Inside Finally Block.");
}
}
}
}
从代码来看,貌似finally块中的println语句应该会被运行5次。
但当程序运行后,你会发现finally块仅仅运行了4次。第5次迭代的时候会触发exit函数的调用,于是这第5次的finally便永远也触发不到了。
原因便是——System.exit会挂起全部线程的运行,包括当前线程。即便是try语句后的finally块,仅仅要是运行了exit,便也无力回天了。
在调用System.exit时。JVM会在关闭前运行两个结束任务:
首先。它会运行全然部通过Runtime.addShutdownHook注冊进来的终止的钩子程序。这一点非常关键。由于它会释放JVM外部的资源。
接下来的便是Finalizer了。可能是System.runFinalizersOnExit也可能是Runtime.runFinalizersOnExit。finalizer的使用已经被废弃有非常长一段时间了。finalizer能够在存活对象上进行调用。即便是这些对象仍在被其他线程所使用。而这会导致不可预期的结果甚至是死锁。
publicclass shutDownHooksDemo {
publicstaticvoid main(String[] args){
for(int i=0;i<5;i++)
{
finalint final_i = i;
try{
Runtime.getRuntime().addShutdownHook(
newThread(){
publicvoid run(){
if(final_i==4){
System.out.println("Inside Try Block.Exiting withoutexecuting Finally block.");
System.exit(0);
}
}
});
}
finally{
System.out.println("InsideFinally Block.");
}
}
}
}
1. 推断奇数
看下这几行代码。看看它们能否用来准确地推断一个数是奇数?
publicboolean oddOrNot(int num){
return num %2==1;
}
看似是对的,可是每运行四便会有一个错误的结果(用数据说话)。
考虑到负奇数的情况。它除以2的结果就不会是1。
因此。返回值是false,而这样是不正确的。
代码能够改动成这样:
publicboolean oddOrNot(int num){
return(num &1)!=0;
}
这么写不光是负奇数的问题攻克了,而且还是经过充分优化过的。由于算术运算和逻辑运行要比乘除运算更高效,计算的结果也会更快。
1. 单引號与双引號的差别
publicclassHaha{
publicstaticvoid main(String args[]){
System.out.print("H"+"a");
System.out.print('H'+'a');
}
}
看起来这段代码会返回”Haha”,但实际返回的是Ha169。
原因就是用了双引號的时候,字符会被当作字符串处理,而假设是单引號的话,字符值会通过一个叫做基础类型拓宽的操作来转换成整型值。然后再将值相加得到169。
1. 一些防止内存泄露的小技巧
内存泄露会导致软件的性能降级。由于Java是自己主动管理内存的,因此开发者并没有太多办法介入。
只是还是有一些方法能够用来防止内存泄露的。
· 查询完数据后马上释放数据库连接
· 尽可能使用finally块
· 释放静态变量中的实例
1. 避免死锁
死锁出现的原因有非常多。避免死锁不是一句话就能解决的。
通常来说,当某个同步对象在等待还有一个同步对象所拥有的资源上的锁时,便会产生死锁。
试着运行下以下的程序。它会告诉你什么是死锁。这个死锁是由于两个线程都在等待对方所拥有的资源,因此会产生死锁。
它们会一直等待,没有谁会先放手。
publicclassDeadlockDemo{
publicstaticObject addLock =newObject();
publicstaticObject subLock =newObject();
publicstaticvoid main(String args[]){
MyAdditionThread add =newMyAdditionThread();
MySubtractionThreadsub=newMySubtractionThread();
add.start();
sub.start();
}
privatestaticclassMyAdditionThreadextendsThread{
publicvoid run(){
synchronized(addLock){
int a =10, b =3;
int c = a + b;
System.out.println("Addition Thread: "+ c);
System.out.println("Holding First Lock...");
try{Thread.sleep(10);}
catch(InterruptedException e){}
System.out.println("Addition Thread: Waiting forAddLock...");
synchronized(subLock){
System.out.println("Threads: Holding Add and SubLocks...");
}
}
}
}
privatestaticclassMySubtractionThreadextendsThread{
publicvoid run(){
synchronized(subLock){
int a =10, b =3;
int c = a - b;
System.out.println("Subtraction Thread: "+ c);
System.out.println("Holding Second Lock...");
try{Thread.sleep(10);}
catch(InterruptedException e){}
System.out.println("Subtraction Thread: Waiting forSubLock...");
synchronized(addLock){
System.out.println("Threads: Holding Add and SubLocks...");
}
}
}
}
}
输出:
AdditionThread:13
SubtractionThread:7
HoldingFirstLock...
HoldingSecondLock...
AdditionThread:WaitingforAddLock...
Subtraction Thread:WaitingforSubLock...
但假设调用的顺序变一下的话,死锁的问题就攻克了。
publicclassDeadlockSolutionDemo{
publicstaticObject addLock =newObject();
publicstaticObject subLock =newObject();
publicstaticvoid main(String args[]){
MyAdditionThread add =newMyAdditionThread();
MySubtractionThreadsub=newMySubtractionThread();
add.start();
sub.start();
}
privatestaticclassMyAdditionThreadextendsThread{
publicvoid run(){
synchronized(addLock){
int a =10, b =3;
int c = a + b;
System.out.println("Addition Thread: "+ c);
System.out.println("Holding First Lock...");
try{Thread.sleep(10);}
catch(InterruptedException e){}
System.out.println("Addition Thread: Waiting forAddLock...");
synchronized(subLock){
System.out.println("Threads: Holding Add and SubLocks...");
}
}
}
}
privatestaticclassMySubtractionThreadextendsThread{
publicvoid run(){
synchronized(addLock){
int a =10, b =3;
int c = a - b;
System.out.println("Subtraction Thread: "+ c);
System.out.println("Holding Second Lock...");
try{Thread.sleep(10);}
catch(InterruptedException e){}
System.out.println("Subtraction Thread: Waiting forSubLock...");
synchronized(subLock){
System.out.println("Threads: Holding Add and SubLocks...");
}
}
}
}
}
输出:
AdditionThread:13
HoldingFirstLock...
AdditionThread:WaitingforAddLock...
Threads:HoldingAddandSubLocks...
SubtractionThread:7
HoldingSecondLock...
Subtraction Thread:WaitingforSubLock...
Threads:HoldingAddandSubLocks...
1. 替Java省点内存
某些Java程序是CPU密集型的,但它们会须要大量的内存。这类程序通常运行得非常缓慢,由于它们对内存的需求非常大。为了能提升这类应用的性能,可得给它们多留点内存。因此,假设我们有一台拥有10G内存的Tomcatserver。在这台机器上。我们能够用例如以下的这条命令来分配内存:
export JAVA_OPTS="$JAVA_OPTS -Xms5000m -Xmx6000m-XX:PermSize=1024m -XX:MaxPermSize=2048m"
· Xms = 最小内存分配
· Xmx = 最大内存分配
· XX:PermSize = JVM启动时的初始大小
· XX:MaxPermSize = JVM启动后可分配的最大空间
1. 怎样计算Java中操作的耗时
在Java中进行操作计时有两个标准的方法:System.currentTimeMillis()和System.nanoTime()。问题就在于,什么情况下该用哪个。从本质上来讲,他们的作用都是一样的,但有以下几点不同:
1. System.currentTimeMillis()的精度在千分之中的一个秒到千分之15秒之间(取决于系统)而System.nanoTime()则能到纳秒级。
2. System.currentTimeMillis读操作耗时在数个CPU时钟左右。
而System.nanoTime()则须要上百个。
3. System.currentTimeMillis相应的是绝对时间(1970年1 月1日所经历的毫秒数),而System.nanoTime()则不与不论什么时间点相关。
4. Float还是double
数据类型 | 所用字节 | 有效位数 |
float | 4 | 7 |
double | 8 | 15 |
在对精度要求高的场景下。double类型相对float要更流行一些,理由例如以下:
大多数处理器在处理float和double上所需的时间都是几乎相同的。而计算时间一样的前提下,double类型却能提供更高的精度。
1. 幂运算
Java是通过异或操作来进行幂运算的。Java对于幂运算有两种处理方式:
· 乘积:
double square =double a *double a; // Optimized
double cube =double a *double a *double a; // Non-optimized
double cube =double a *double square; // Optimized
double quad =double a *double a *double a *double a; // Non-optimized
double quad =double square *double square; // Optimized
· pow方法:在无法使用乘积的情况下能够使用pow方法。
double cube =Math.pow(base, exponent);
不到万不得已不要使用Math.pow。
例如说,当指数是小数的时候。由于Math.pow要比乘积慢300-600倍左右。
1. 怎样处理空指针异常
空指针异常是Java中非经常见的异常。
当你尝试调用一个null对象上的方法时便会抛出这个异常。例如:
int noOfStudents = school.listStudents().count;
在上述样例中,school为空或者listStudents()为空都可能会抛出了NullPointerException。
因此最好检查下对象是否为空以避免相似情况。
privateint getListOfStudents(File[] files){
if(files ==null)
thrownewNullPointerException("File list cannot be null");
}
1. JSON编码
JSON是数据存储及传输的一种协议。与XML相比,它更易于使用。由于它非常轻量级以及自身的一些特性。如今JSON在网络上已经是越来越流行了。常见的数据结构都能够编码成JSON然后在各个网页间自由地传输。只是在開始编码前。你得先安装一个JSON解析器。在以下的样例中。我们将使用json.simple库来完毕这项工作 (https://code.google.com/p/json-simple/)。
以下是编码成JSON串的一个简单的样例。
import org.json.simple.JSONObject;
import org.json.simple.JSONArray;
publicclassJsonEncodeDemo{
publicstaticvoid main(String[] args){
JSONObject obj =newJSONObject();
obj.put("Novel Name","Godaan");
obj.put("Author","Munshi Premchand");
JSONArray novelDetails =newJSONArray();
novelDetails.add("Language:Hindi");
novelDetails.add("Year ofPublication: 1936");
novelDetails.add("Publisher:Lokmanya Press");
obj.put("Novel Details", novelDetails);
System.out.print(obj);
}
}
输出:
{"Novel Name":"Godaan","Novel Details":["Language: Hindi","Year of Publication: 1936","Publisher: Lokmanya Press"],"Author":"Munshi Premchand"}
1. JSON解析
开发者要想解析JSON串。首先你得知道它的格式。以下样例有助于你来理解这一点:
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Iterator;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
publicclassJsonParseTest{
privatestaticfinalString filePath ="//home//user//Documents//jsonDemoFile.json";
publicstaticvoid main(String[] args){
try{
// read the json file
FileReader reader =newFileReader(filePath);
JSONParser jsonParser =newJSONParser();
JSONObject jsonObject =(JSONObject)jsonParser.parse(reader);
// get a number from the JSON object
Long id = (Long) jsonObject.get("id");
System.out.println("The id is: "+ id);
// get a String from the JSON object
String type =(String) jsonObject.get("type");
System.out.println("The type is: "+ type);
// get a String from the JSON object
String name =(String) jsonObject.get("name");
System.out.println("The name is: "+ name);
// get a number from the JSON object
Double ppu = (Double) jsonObject.get("ppu");
System.out.println("The PPU is: "+ ppu);
// get an array from the JSON object
System.out.println("Batters:");
JSONArray batterArray=(JSONArray) jsonObject.get("batters");
Iterator i = batterArray.iterator();
// take each value from the json arrayseparately
while(i.hasNext()){
JSONObject innerObj =(JSONObject) i.next();
System.out.println("ID "+ innerObj.get("id")+
" type"+ innerObj.get("type"));
}
// get an array from the JSON object
System.out.println("Topping:");
JSONArray toppingArray=(JSONArray) jsonObject.get("topping");
Iterator j = toppingArray.iterator();
// take each value from the json arrayseparately
while(j.hasNext()){
JSONObject innerObj =(JSONObject) j.next();
System.out.println("ID "+ innerObj.get("id")+
" type"+ innerObj.get("type"));
}
}catch(FileNotFoundException ex){
ex.printStackTrace();
}catch(IOExceptionex){
ex.printStackTrace();
}catch(ParseExceptionex){
ex.printStackTrace();
}catch(NullPointerException ex){
ex.printStackTrace();
}
}
}
jsonDemoFile.json
{
"id":0001,
"type":"donut",
"name":"Cake",
"ppu":0.55,
"batters":
[
{"id":1001,"type":"Regular"},
{"id":1002,"type":"Chocolate"},
{"id":1003,"type":"Blueberry"},
{"id":1004,"type":"Devil's Food"}
],
"topping":
[
{"id":5001,"type":"None"},
{"id":5002,"type":"Glazed"},
{"id":5005,"type":"Sugar"},
{"id":5007,"type":"Powdered Sugar"},
{"id":5006,"type":"Chocolate with Sprinkles"},
{"id":5003,"type":"Chocolate"},
{"id":5004,"type":"Maple"}
]
}
The id is:1
The type is: donut
The name is:Cake
The PPU is:0.55
Batters:
ID 1001 type Regular
ID 1002 type Chocolate
ID 1003 type Blueberry
ID 1004 type Devil's Food
Topping:
ID 5001 type None
ID 5002 type Glazed
ID 5005 type Sugar
ID 5007 type Powdered Sugar
ID 5006 type Chocolate with Sprinkles
ID 5003 type Chocolate
ID 5004 type Maple
1. 简单字符串查找
Java提供了一个库函数叫做indexOf()。这种方法能够用在String对象上,它返回的是要查找的字符串所在的位置序号。
假设查找不到则会返回-1。
1. 列出文件夹下的文件
你能够用以下的代码来列出文件夹下的文件。
这个程序会遍历某个文件夹下的全部子文件夹及文件,并存储到一个数组里,然后通过遍历数组来列出全部文件。
import java.io.*;
publicclassListContents{
publicstaticvoid main(String[] args){
File file =newFile("//home//user//Documents/");
String[] files = file.list();
System.out.println("Listingcontents of "+ file.getPath());
for(int i=0; i < files.length ; i++)
{
System.out.println(files[i]);
}
}
}
1. 一个简单的IO程序
Java提供了FileInputStream以及FileOutputStream类来进行文件的读写操作。
FileInputStream的构造方法会接收输入文件的路径作为入參然后创建出一个文件的输入流。相同的。FileOutputStream的构造方法也会接收一个文件路径作为入參然后创建出文件的输出流。
在处理完文件之后,一个非常重要的操作就是要记得”close”掉这些流。
import java.io.*;
publicclass myIODemo {
publicstaticvoid main(String args[])throwsIOException{
FileInputStreamin=null;
FileOutputStreamout=null;
try{
in=newFileInputStream("//home//user//Documents//InputFile.txt");
out=newFileOutputStream("//home//user//Documents//OutputFile.txt");
int c;
while((c =in.read())!=-1){
out.write(c);
}
}finally{
if(in!=null){
in.close();
}
if(out!=null){
out.close();
}
}
}
}