搜尋

Epic

分類

java

Java新手面試題目

看到有人發了一張面試的題目:

所以就想說順便複習基本概念,所以寫了解答:12983952_10207736373520319_4097456700067327781_o.jpg

Java新手面試題目答案

 

1-1.封裝(encapsulation):隱藏類別的實作細節,僅提供合法的interface給其他類別呼叫,例如常見的資料傳輸物件DTO(data transfer object)會把要傳送的資料封裝在物件裡面,要修改或取值必須要呼叫Setter或Getter。與此相關的存取修飾是public、protected與private。

 

1-2.繼承(inherit):為了簡化類別的設計,通常會把類似的幾個類別的共同的屬(field)及方法(method)抽取出來成為父類別(superclass),再把客製化的部分用變成子類別,兩者關係用子類別extends 父類別來連結。例如bird extends animal

 

1-3.多型(polymorphism):一旦有繼承關係。父類別可以當作子類別的型態。這樣日後要修改維護時,只需要修改父類別就好,而不需要動到不同的子類。例如bird extends animal,建立實例時會寫成bird birdA=new bird();多型的方式就會寫成animal birdA=new bird();

 

 

2.ClassPath:就是指定執行這個package的根目錄給JVM。如果要引用別的package,import X.XX.* 像這樣ClassPath+X.XX會等於package的絕對路徑。例如 import a.bc.* 而classPath= c:\\lib則package的路徑是c:\\lib\\a\\bc\\。

 

3-1.Loop迴圈

int sum=0;

for(int i=0;i<=100;i++)

{

sum += i++;

}

 

3-2.While迴圈

int sum = 0;

int i = 1;

while (i <= 100)

{

sum += i++;

}

 

3-3.Do-while 迴圈

int sum = 0;

int i = 1;

do

{

sum += i++;

} while (i <= 100);

 

4-1.

If-else

Switch

 

4-2.

If(age<20)

return “青少年”;

if(age<30&&age>20)

return “壯年”;

if(age<50&&age>30)

return “中年”;

if(age>50)

return “老年”;

 

5.Wrapper Class: 是將八大基本資料型類別的物件化,例如Integer a=new Integer(“12345”); 可以方便作資料型轉換。應用的話設計模式有: Adaptor和Decorator。

6.==是兩個參考指向同一物件返回true否則else。equals則是可以自訂相同的條件,如果沒有,則系統直接使用Object的equals。而Object的equals和==的比較方式相同。

持久層(Data Persistence Layer)

「持久」的意思是把數據「固態化」到硬碟上的一個過程,一般實現數據持久化的方法可以是檔案IO,可以是JDBC對資料庫的操作。而所謂的「持久層」意思是把數據儲存的相關操作從原本的架構中解耦(解耦的意思是降低事務之間的關聯性)抽離出來,獨立成一個處理持久邏輯的層。類似底下這張架構圖。

圖片1EEE

邏輯層的操作不會直接到數據層去做CURD而是透過持久層去操作,這樣的架構的好處是一方面可以統一對資料庫的存取,二方面可以避免邏輯層對數據的過度干預。

持久層的主流解決方案

1.JDBC搭配DAO/DTO

2.JDO(Java Data Object)

3.ORM:相關框架有Hibernate

資料傳輸物件 DTO(data transfer object)

也稱作value object

定義:

引述自Martin Folwer的Patterns of Enterprise Application Architecture
An object that carries data between processes in order to reduce the number of method calls.

優點:

1.減少參數傳遞的混亂,增加可讀性
2.方便修改參數傳遞之後的維護
3.封裝資訊,將必要傳遞但不希望被操作的資料封裝起來
4.擴充性,如果將來這組要傳遞的資料需要增加其他欄位或條件,只需要增加物件的屬性就可以完成整體的擴充

設計模式:

可以用解釋器模式+設定檔+裝飾模式構造DTO的資料結構,然後透過設定檔生成統一的DTO物件提供資料傳輸

LMS最小平方學習法(機器學習)

/**
 * 測試LMS
 * @author whuang022
 */
public class LMSTest
{
    public static void main(String[] args)
    {
        LoadTrainData  LoadTrainData =new LoadTrainData ();
        LMS LMSRefact=new LMS(0.08);//學習率
        LMSRefact.doLMS( LoadTrainData.getData(&quot;C:\\Users\\user\\Desktop\\train.txt&quot;,4),0.8);//訓練資料,閥值
    }

}
/**
 * 主要計算LMS的物件
 * @author whuang022
 */
public class LMS 
{
    private   double rho;
    public LMS(double rho)//給定學習率
    {
        this.rho=rho;
    }
    public double Fx(  LMSMritx  test , double weights[])//計算函數的輸出結果
    {
        double sum = 0.0;
        sum +=  test.x1*weights[0];//第一項
        sum +=  test.x2*weights[1];//第二項
        sum += weights[2] * 1.0;//常數
        return sum;
    }
    public double getMeanSquareError(LMSMritx test[], double weights[])//計算均方差(Mean Square Error)
    {
        double sum = 0.0;
        for (int i = 0; i < 2; ++i)
        {
            sum += Math.pow(test[i].y- Fx(test[i], weights), 2);
        }
        return sum / (double)test.length;
    }
    public double [] doLMS( LMSMritx[] test,double Threshold)//訓練資料,閥值
    {
        double []weights =  new  double[test.length-1];
         for(int i=0;i<weights.length;i++)
         {
             weights[i]=0.0;
         }
    while (true) 
    {    
        //隨機選一組輸入的X Y對
        int i =(int)(Math.random()*(test.length-1)+1);//1,2,3
        double Y0=0;
        Y0=Fx(test[i],weights);//Y0=計算輸出(輸入對的X,權重)
        double err=test[i].y-Y0;//計算輸出差(Y-Y0)
        //新權重=舊權重+學習速率*輸出差*輸入的X 
        weights[0]= weights[0]+rho * err*test[i].x1;
        weights[1]= weights[1]+rho * err*test[i].x2;
        weights[2]= weights[2]+rho * err;
        //計算平均差異(加總新輸出差(Y-Y0)^2)/總測資
         if(getMeanSquareError(test,weights)<Threshold)//誤差小於閥值就跳出
         {
            break;
         }
    }
        //完成訓練,輸出結果
    for (int w = 0; w < weights.length; ++w)
    {
        System.out.println("weight "+w+":"+weights[w]);//顯示權重
    }
         
    for (int i = 0; i < 4; ++i)
    {
        System.out.print( "right result:" + test[i].y+"\t");//顯示預期輸出
        System.out.print( "predict result:"+Fx(test[i], weights)+"\n");//顯示實際輸出
    }
       
        return weights;//回傳方便保存
    }
}
/**
 * 存放訓練資料用的單位
 * @author whuang022
 */
public class LMSMritx 
{
    double x1=0;
    double x2=0;
    double y=0;
}

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
/**
 *載入訓練資料
 * @author whuang022
 */
public class LoadTrainData 
{
    
    public LMSMritx[] getData(String fileName,int num)//檔案位置,訓練資料數量
    {
       LMSMritx test[]=new LMSMritx[num];
       try 
       {
           BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(fileName),"BIG5"));
           String file;
           int i=0;
           while((file = br.readLine())!=null)
           {
                String [] term;
                term=file.split("@");
                LMSMritx testAtom=new LMSMritx();
                testAtom.x1=Double.parseDouble(term[0]);//X1
                testAtom.x1=Double.parseDouble(term[1]);//X2
                testAtom.y=Double.parseDouble(term[2]);//Y
                test[i]=testAtom;
                i++;
           }
       }
       catch (IOException ex)
       {
          System.out.println("IOException!");
       }   
        return test;
    }
}

測資:train.txt

3@2@-1
6@5@2
9@3@0.5
13@14@1

輸出結果:

run:
weight 0:0.22661785599999998
weight 1:0.0
weight 2:-0.003906048000000023
right result:-1.0	predict result:0.44932966399999996
right result:2.0	predict result:1.129183232
right result:0.5	predict result:0.6759475199999999
right result:1.0	predict result:3.1687439359999994
BUILD SUCCESSFUL (total time: 0 seconds)

Log4J系統除錯日誌

蓋大型系統時,為了要能夠在日後系統上線試運的時候可以有系統地抓到潛在的Bug,就可以使用Log4JSlf4j等相關框架來實現自動的除錯日誌。

Log4J:

要取得Log4可以用Maven配置:


<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>

或是直接下載Jar配置也可以。

Log4J的日誌等級有五種(DEBUG, INFO, WARN, ERROR, FATAL),我把這五種等級看作嚴重性的表示,當設定為WARN時,就會輸出WARN~ FATAL的訊息.而DEBUG和 INFO就會被忽略不輸出.而語法:


log4j.rootLogger=WARN

類似這種設定的資料為了方便操作,所以一般都會集中到Log4J的設定檔裡面.設定檔的位置放在專案的根目錄,並命名為log4j.properties 而詳細的設定檔語法可以參考Log4j配置文件基本含义说明Java:Log4j 的簡單教學這兩篇文章。

接著,在程式碼中需要被日誌紀錄的地方使用:

private static Logger log= Logger.getLogger(MainTest.class);//記得把MainTest換成該類別的名稱
log.debug("Debug");//把這行放置在可能出錯需要被記錄的位置 例如try catch 的 catch裡面

另外,日誌不但可以直接輸出成檔案

log4j.appender.file=org.apache.log4j.RollingFileAppender

也可以輸出到資料庫裡存放

log4j.appender.DB=org.apache.log4j.jdbc.JDBCAppender

(如果是做伺服器之類的,應該超需要這個功能!)

所以廢話不多說,我參考:Log4j JDBCAppender – Create Logs in Database這篇實作了一個範例:

pom.xml(Maven設定檔)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany</groupId>
<artifactId>Log4JTry</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
</dependencies>
</project>

 

MainTest.java 主要的程式碼:
package com.mycompany.log4jtry;
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.FileAppender;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import org.apache.log4j.SimpleLayout;
import org.apache.log4j.xml.DOMConfigurator;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class MainTest 
{
 private static Logger log= Logger.getLogger(MainTest.class);
 public static void main(String[] args) throws InterruptedException
 {
 String log4jConfPath = "log4j.properties"; 
 PropertyConfigurator.configure(log4jConfPath);
 log.debug("Debug");
 log.info("Info");
 Thread.sleep(2000);
 }

}

log4j.properties 設定檔

log4j.rootLogger = DEBUG, DB

log4j.appender.DB=org.apache.log4j.jdbc.JDBCAppender

log4j.appender.DB.URL=jdbc:mysql://localhost/資料庫名稱

log4j.appender.DB.driver=com.mysql.jdbc.Driver

log4j.appender.DB.user=資料庫帳號

log4j.appender.DB.password=資料庫密碼

log4j.appender.DB.sql=INSERT INTO LOGS VALUES('%x','%d','%C','%p','%m')

log4j.appender.DB.layout=org.apache.log4j.PatternLayout

以及 SQL 建表的部分:

<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> <span class="hljs-keyword">LOGS</span>
(USER_ID <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">20</span>) <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>,
DATED
<span class="hljs-built_in">DATE</span> <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>,
LOGGER <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">50</span>) <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>,
<span class="hljs-keyword">LEVEL</span>
<span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">10</span>) <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>,
MESSAGE <span class="hljs-built_in">VARCHAR</span>(<span class="hljs-number">1000</span>) <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>
);

執行之後就可以看到:

sql1
(因為Date的格式我沒設好 所以建表之後有先把DATED欄位改varchar(50) (懶~`~)

這樣日後對分析剛上線的服務應該會相當有幫助!

Java-Polymorphism多型

 

多型的意思是,透過繼承和覆寫機制,讓呼叫者send出去的訊息,引發接收者的不同反應。決定權在接收者,而不在呼叫者。

 

從抽象的角度來看,多型就是表面上都呼叫同一個方法,但產生的實際型完卻不一樣。以下的範例透過robotInterface定義出robot的介面,而robotIm實作robotInterface,把robot的方法都實作完成。然後我小變形的一下,讓robotIm2繼承robotIm,再透過覆寫把一樣的方法定義上不同的動作,一樣的方法compute就直接繼承而來,不去贅述。

 

然後我又定了show這個方法,去組合robotInterface所定之方法。這時候 show(A)和show(B)都可以依順序引發compute(1)、 work(1)、walk(1),但除了compute的實際行為一樣,work(1)、walk(1)分別引發: robotImA-work、robotImA-walk和robotImB-work、robotImB-walk。這就達到了呼叫方法一樣,但引發的實際行為不同。

實際運行結果:

compute

robotImA-work

robotImA-walk

compute

robotImB-work

robotImB-walk

這裡的好處就是,我只要修改show這個方法的內容,就可以修改上層邏輯,比如說改成:

a.work(1);

a.walk(1);

a.compute(1);

運行結果就會變成:

robotImA-work

robotImA-walk

compute

robotImB-work

robotImB-walk

compute

這樣可以增加整體的擴充性。

另外,以後robot物件通通實作 robot的介面,只要傳入show,方法該怎麼被呼叫就會固自動定下來,可以省掉不少問題。

以下是程式碼:
JavaApplication73.java

package javaapplication73;

public class JavaApplication73
{

public static void main(String[] args)
{
robotIm A=new robotIm();
show(A);
robotIm2 B=new robotIm2();
show(B);
}
public static void show(robotInterface a)
{
a.compute(1);
a.work(1);
a.walk(1);
}
}

robotInterface.java

package javaapplication73;

public interface robotInterface
{
public void compute(int type);
public void work(int times);
public void walk(int steps);

}

robotIm.java

package javaapplication73;

public class robotIm implements robotInterface
{

@Override
public void compute(int type)
{
System.out.println("compute");
}

@Override
public void work(int times)
{
System.out.println("robotImA-work");
}

@Override
public void walk(int steps)
{
System.out.println("robotImA-walk");
}

}

robotIm2.java

package javaapplication73;

public class robotIm2 extends robotIm
{

@Override
public void work(int times)
{
System.out.println("robotImB-work");
}

@Override
public void walk(int steps)
{
System.out.println("robotImB-walk");
}

}

參考資料:論物件導向part 5:Polymorphism

決策樹C4.5分析與Java實作

C4.5演算法是一種用來對數據實作出決策樹(Decision tree analysis)分析的演算法,由Ross Quinlan於1993改進ID3演算法(Ross Quinlan,1979)提出.決策樹的分析可以處做出條件判斷式(也就是所謂的邏輯模型),用來對資料做預測,這樣的好處是生成的條件判斷可以讓人看得懂,有助於做出商業邏輯的判斷和理解,缺點是由於要做好幾次排序處理,所以效率比較低

C4.5演算法的分割規則是用:資訊熵(Entropy)和資訊獲利比率(Gain Ratio),虛擬碼參考自維基百科:

  1. Check for base cases
  2. For each attribute a
    1. Find the normalized information gain ratio from splitting on a
  3. Let a_bestbe the attribute with the highest normalized information gain
  4. Create a decision nodethat splits on a_best
  5. Recur on the sublists obtained by splitting on a_best, and add those nodes as children of node

實做的部分利用Weka平台,現在假設我要分析一款App是不是能夠成功被市場接受,已有的歷史資料如下:

X

(我這裡有省略13之後的資料,這份資料是自己亂數key的,只是demo用,沒有參考價值)

接著要轉換成weka看得懂的格式
@relation XXX表示分析的主題,
@attribute YYY表示分析的參數名稱
(numeric是數字,{}是集合)以下是範例:


@relation AppAnalysisTest

@attribute AppAttribute {social,tool,game}
@attribute AppStartUsers numeric
@attribute FirstMonthIncreasUser numeric
@attribute Pivot{yes, no}
@attribute Success{yes, no}

@data
social,10,50,yes,no
tool,1,79,no,yes
game,75,99,yes,yes
social,134,1020,no,yes
game,34,10009,yes,yes
social,0,803,yes,no


一樣有省略掉資料,我的訓練資料可以下載:

Excel資料檔

Weka資料檔

接著是程式碼的部分:

package javaapplication58;

import java.awt.BorderLayout;
import java.io.BufferedReader;
import java.io.FileReader;
import javax.swing.JFrame;
import weka.classifiers.trees.J48;
import weka.core.Instances;
import weka.gui.treevisualizer.PlaceNode2;
import weka.gui.treevisualizer.TreeVisualizer;


public class JavaApplication58 
{


    
    public static void main(String args[]) throws Exception 
    {
        BufferedReader reader = new BufferedReader( new FileReader("App.arff"));
        //讀檔
        J48 AnsC45=new J48();//C4.5的分析物件在weka.jar上叫做J48()
        Instances data = new Instances(reader);//把資料放進來
        reader.close();
        data.setClassIndex(data.numAttributes()-1);
        AnsC45.buildClassifier(data);//這裡做分析
        System.out.println(AnsC45.toSummaryString());//印出結論
        //視覺化分析結果
        TreeVisualizer tv=new TreeVisualizer(null,AnsC45.graph(),new PlaceNode2());
        JFrame visual=new JFrame("Example");//視窗名稱
        visual.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);  
        visual.setSize(800,600);//視窗大小
        visual.getContentPane().add(tv,BorderLayout.CENTER);//加入圖表
        visual.setVisible(true);
        //
    }
    
}

跑出來的結果如下:

run:
Number of leaves: 4
Size of the tree: 6

以及

SX

最後的解讀,一個可能可以成功的App的模式可以是一初始使用者人數大於10,如果小於10則只有工具類型的才會成功(以上分析純屬唬爛,因為數據是掰的)

另外,如果想要跑數據預測可以參考這裡

從一個個案探討例外處理架構

最近剛幫一個朋友解bug原始碼:
最後學到了一些設計架構上的教訓,決定來整理一篇文章談這件事情,
原始碼裡面有如下的方法:

public String GetURLData()
{
	 // 讀取網頁資料,並將資料存放在 urlData 
	urlData = null; 
	 try { 
			url = new URL(WebURL); 
		    httpUrlconnection = (HttpURLConnection) url.openConnection(); 
		    httpUrlconnection.setDoInput(true); 
		    httpUrlconnection.setDoOutput(true); 
		    httpUrlconnection.connect();
		    BufferedReader bufReader = new BufferedReader(new InputStreamReader(httpUrlconnection.getInputStream())); 
		    String decodedString; 
			 while ((decodedString = bufReader.readLine()) != null) { 
				 urlData += decodedString; 
			 } 
			 bufReader.close();
			 httpUrlconnection.disconnect();
   } 
	catch(Exception e)
	{ 
		 Log.e("ERROR", e.toString()); 
	} 
	 return urlData;
} 

這裡面先是把urlData = null;然後try…catch最後return urlData;這樣的架構有個問題是發生catch之後仍然會把null傳出去,會導致下面使用這個urlData的class只要做任何操作幾乎都會出現Nullpointerexception 本題是app的程式,所以直接閃退給你看,我認為這樣的架構對除錯的而言比較不好處理,因該要把urlData賦值在傳出去,而操作前也要檢查該值是不是正確的,才做處理.

另外Teddy前輩有寫過相關的處理方式找不到資料要傳回Null還是丟出Exception? 則是認為,回傳NULL沒有問題

Jaccard similarity coefficient與Java

XXd

Jaccard similarity是用來計算某個兩個東西的相似度有多少,假設上表的1-7項目表示七種食物,ABCD分別表示四個人,1表示喜歡0表示不喜歡,我就可以利用Jaccard similarity來了了解B和C的喜好有多相似,Jaccard similarity長應用在機器學習裡面

 

Jaccard similarity的計算公式:J(A,B)=(A交集B)/(A聯集B)


以下是Java的實作範例
JaccardSimilarity.java:

package jaccardsimilarity;

import lib.Jaccard;

/**
 *Jaccard Similarity 計算實作
 */
public class JaccardSimilarity 
{

    public static void main(String[] args) 
    {
        boolean mix[][]=
        {
            {false,true,true,true},
            {true,true,false,true},
            {true,false,true,false},
            {false,false,false,true},
            {true,true,true,false},
            {false,true,true,false},
            {true,false,true,false},
        };
         Jaccard  JaccardA=new Jaccard(mix);
         System.out.println(JaccardA.JaccardCount(1, 2));//B C的Jaccard similarity
         System.out.println(JaccardA.JaccardCount(0, 3));//A D的Jaccard similarity
    }
    
}

Jaccard.java

package lib;

public class Jaccard 
{
    private boolean data[][];
    public Jaccard(boolean mix[][])
    {
        this.data=mix;
    }
    public float JaccardCount(int x,int y)//要運算的兩個項目編號(0-N)
    {
        float andNum=0,orNum=0;
        int d=data.length;
        float Jac;
        for(int i=0;i<d;i++)
        {
            if(data[i][x]&data[i][y])//求交集數量
            {
                andNum++;
            }
            if(data[i][x]|data[i][y])//求連集數量
            {
                orNum++;
            }
        }
        Jac= andNum/orNum;
       return  Jac;
    }

}

輸出結果:
run:
0.5
0.16666667
 

 

Java 例外處理架構

Java的錯誤處理機制採用try…catch…finally.這樣的結構去處理.在try裡面執行有可能會出例外的敘述, catch裡面是假設try出錯的話會離開try區塊跳到catch裡面執行(這裡可以放出錯提示等等)最後不管有沒有出例外,都會執行finally區塊裡面的敘述.

當發生錯誤時,會給出Exception相關的物件告訴程式設計師出例外了,這裡有一個錯誤物件的架構圖可以參考:

SSSAAA

這裡面,所有的物件都繼承Throwable這個物件,而出現的所有例外則都繼承自Exception 所以任何例外都可以用Exception接住,

以下示範:

package exceptionexample;
public class ExceptionExample 
{
    public static void main(String[] args) 
    {
        try 
        {
            int i=10; int j=0;//嘗試執行某敘述
            System.out.println(i/j);
        }
        catch(Exception e)
        {
            System.out.println(e); //捕捉錯誤
        }
        finally
        {
            System.out.println("Finally");
            //最後處理
        }

    }
    
}

輸出結果:

run:
java.lang.ArithmeticException: / by zero
Finally

在 WordPress.com 建立免費網站或網誌.

向上 ↑