1. 程式人生 > >JAVA第三週作業--遇到的問題及解決方案

JAVA第三週作業--遇到的問題及解決方案

1,java中的Scanner類用法:

用Scanner類

import java.util.Scanner;

public static void main(String [] args) { 
         Scanner sc = new Scanner(System.in); 
         System.out.println("請輸入你的姓名:"); 
         String name = sc.nextLine(); 
         System.out.println("請輸入你的年齡:"); 
         int age = sc.nextInt(); 
         System.out.println("請輸入你的工資:"); 
         float salary = sc.nextFloat(); 
         System.out.println("你的資訊如下:"); 
         System.out.println("姓名:"+name+"\n"+"年齡:"+age+"\n"+"工資:"+salary); 

}

這段程式碼已經表明,Scanner類不管是對於字串還是整型資料或者float型別的變數,只需做一點小小的改變,就能夠實現功能!無疑他是最強大的!

但是,在使用第三種輸入方法的時候有一個需要注意的地方,就是nextLine()函式,在io包中有一個和他功能一樣的函式我next()函式,他們的功能一樣,但是在實現上有什麼差別呢,請看下面程式碼:

public static void main(String [] args) { 
         Scanner sc = new Scanner(System.in); 
         System.out.println("請輸入你的年齡:"); 
         int age = sc.nextInt(); 
         System.out.println("請輸入你的姓名:"); 
         String name = sc.nextLine(); 
         System.out.println("請輸入你的工資:"); 
         float salary = sc.nextFloat(); 
         System.out.println("你的資訊如下:"); 
         System.out.println("姓名:"+name+"\n"+"年齡:"+age+"\n"+"工資:"+salary); 
}

這段程式碼和上邊實現輸入方法給出的例子程式碼區別在於,這段程式碼先執行nextInit()再執行nextLine(),而上述的例子是先執行nextLine(),再執行nextInit(),當你在執行著兩段程式碼的時候你會發現第三種方法的例子可以實現正常的輸入,而這段程式碼卻在輸入年齡,敲擊enter鍵後,跳過了輸入姓名,直接到了輸入工資這裡,(可以自己執行程式碼看看)這是為什麼呢?其實,在執行nextInit()函式之後,敲擊了enter回車鍵,回車符會被nextLine()函式吸收,實際上是執行了nextLine()函式吸收了輸入的回車符(並不是沒有執行nextLine函式),前面講到和nextLine()功能一樣的函式next(),他們的區別就在於:next()函式不會接收回車符和tab,或者空格鍵等,所以在使用nextLine()函式的時候,要注意敲擊的回車符有個被其吸收,導致程式出現BUG!!!

最後小小的總結一下next()和nextLine()的區別:

在java中,next()方法是不接收空格的,在接收到有效資料前,所有的空格或者tab鍵等輸入被忽略,若有有效資料,則遇到這些鍵退出。nextLine()可以接收空格或者tab鍵,其輸入應該以enter鍵結束

2,執行時的BUG一號:

網上有針對於它的解決辦法,不過沒學會,將整個檔案轉移到另一個常用的寫java的地方去,再用IDEA開啟,就o雞兒k了。

3,java中的開方函式

java中有一個Math類裡面很多靜態的方法,都是一些常見的數學公式的實現,例如求平方根的Math.sqrt(n),求a的b次方Math.pow(a, b),求絕對值Math.abs(n)等很多。下面是一些演示。

public class MathTest
{
    public static void main(String[] args)
    {
        int n = 16;
        System.out.println(Math.sqrt(n));
        System.out.println(Math.pow(2, 3));
        System.out.println(Math.abs(-4));
        System.out.println(Math.log10(100));
    }
}

開N次方:

import java.text.DecimalFormat;

public class Test82 {
    public static void main(String[] args) {
        double y=0;
        double x=512;
        int n=3;

        y=java.lang.StrictMath.pow(x,1.0/n);
        DecimalFormat bd=new DecimalFormat("########");//把double四捨五入取整

        System.out.println(bd.format(y));
    }
}

4,java中定義常量

    用final修飾符

    在Java語言中,主要是利用final關鍵字來定義常量。當常量被設定後,一般情況下就不允許再進行更改。如可以利用如下的形式來定義一個常量:final double PI=3.1315。在定義這個常量時,需要注意如下內容:

一是常量在定義的時候,就需要對常量進行初始化。也就是說,必須要在常量宣告時對其進行初始化。都跟區域性變數或者成員變數不同。當在常量定義的時候初始化過後,在應用程式中就無法再次對這個常量進行賦值。如果強行賦值的話,會跳出錯誤資訊,並拒絕接受這一個新的值。

二是final關鍵字使用的範圍。這個final關鍵字不僅可以用來修飾基本資料型別的常量,還可以用來修飾物件的引用或者方法。如陣列就是一個物件引用。為此可以使用final關鍵字來定義一個常量的陣列。這就是Java語言中一個很大的特色。一旦一個數組物件被final關鍵字設定為常量陣列之後,它只能夠恆定的指向一個數組物件,無法將其改變指向另外一個物件,也無法更改陣列中的值。

三是需要注意常量的命名規則。不同的語言,在定義變數或者常量的時候,都有自己一套編碼規則。這主要是為了提高程式碼的共享程度與提高程式碼的易讀性。在Java語言中,定義常量的時候,也有自己的一套規則。如在給常量取名的時候,一般都用大寫字元。在Java語言中,大小寫字元是敏感的。之所以採用大寫字元,主要是跟變數進行區分。雖然說給常量取名時採用小寫字元,也不會有語法上的錯誤。但是,為了在編寫程式碼時能夠一目瞭然的判斷變數與常量,最好還是能夠將常量設定為大寫字元。另外,在常量中,往往通過下劃線來分隔不同的字元。而不想物件名或者類名那樣,通過首字元大寫的方式來進行分隔。這些規則雖然不是強制性的規則,但是為了提高程式碼友好性,方便開發團隊中的其他成員閱讀,這些規則還是需要遵守的。沒有規矩,不成方圓。

總之,Java開發人員需要注意,被定義為final的常量需要採用大寫字母命名,並且中間最好使用下劃線作為分隔符來進行連線多個單詞。在定義final的資料不論是常量、物件引用還是陣列,在主函式中都不可以改變。否則的話,會被器拒絕並提示錯誤資訊。

由於Javal是面向物件的語言,所以在定義常量的時候還有與其它程式語言不同的地方。如一段程式程式碼從到最後執行,即使需要經過兩個過程,分別為程式碼的裝載與物件的建立。不同的過程對於常量的影響是不同的。現在假設有如下的程式碼:

Private static Random rd1=new Random; //例項化一個隨機數生成物件。

Private final int int1=rd1.nestInt; //生成隨機數並賦值給常量int1

Private static final int int2=rd1.nestInt; //生成隨機數並賦值給常量int2

這上面的語句的大致含義是,通過Java語言提供的隨機數類物件,生成隨機數。並把生成的隨機數賦值給常量int1與int2。細心的讀者會發現,雖然同樣是賦值語句,但是以上兩個語句中有一個細小的差別,即在第二條語句中多了一個關鍵字static。關於關鍵字的用途,在以前的文章中也有談到過。這個是一個靜態的概念。即當利用這個關鍵字來修飾一個變數的時候,在建立物件之前就會為這個變數在記憶體中建立一個儲存空間。以後建立對物件如果需要用到這個靜態變數,那麼就會共享這一個變數的儲存空間。也就是說,在建立物件的時候,如果用到這個變數,那麼系統不會為其再分配一個儲存空間,而只是將這個記憶體儲存空間的地址賦值給他。如此做的好處就是可以讓多個物件採用相同的初始變數。當需要改變多個物件中變數值的時候,只需要改變一次即可。從這個特性上來說,其跟常量的作用比較類似。不過其並不能夠取代常量的作用。

那麼以上兩條語句有什麼差別嗎?我們首先來看Private final int int1=rd1.nestInt這條語句。雖然int1也是一個常量,但是其是在物件建立的時候初始化的。如現在需要建立兩個物件,那麼需要對這個變數初始化兩次。而在兩次物件初始化的過程中,由於生成的隨機數不同,所以常量初始化的值也不同。最後導致的結果就是,雖然int1是常量,但是在不同物件中,其值有可能是不同的。可見,定義為final的常量並不是恆定不變的。因為預設情況下,定義的常量是在物件建立的時候被初始化。如果在建立常量時,直接賦一個固定的值,而不是通過其他物件或者函式來賦值,那麼這個常量的值就是恆定不變的,即在多個物件中值也使相同的。但是如果在給常量賦值的時候,採用的是一些函式或者物件,那麼每次建立物件時其給常量的初始化值就有可能不同。這往往是人員不原意看到的。有時候人員希望建立再多的物件,其在多個物件中引用常量的值都是相同的。

要是現這個需求的話,有兩個方法。一是在給常量賦值的時候,直接賦予一個固定的值,如abcd等等。而不是一個會根據環境變化的函式或者物件。像生成隨機數的物件,每次執行時其結果都有可能不能。利用這個物件來對常量進行初始化的時候,那麼結果可能每次建立物件時這個結果都有可能不同。最後這個常量只能夠做到在一個物件內是恆定不變的,而無法做到在一個應用程式內是恆定不變的。另外一個方法就是將關鍵字static與關鍵字final同時使用。一個被定義為final的物件引用或者常量只能夠指向唯一的一個物件,不可以將他再指向其他物件。但是,正如上面舉的一個隨機數的例子,物件本身的內容的值是可以改變的。為了做到一個常量在一個應用程式內真的不被更改,就需要將常量宣告為staitc final的常量。這是什麼意思呢?正如上面所說的,當執行一個應用程式的時候,可以分為兩個步驟,分別為程式碼裝載與物件建立。為了確保在所有情況下應用程式還能夠得到一個相同值的常量,那麼就最好告訴編譯器,在程式碼裝載的時候就初始化常量的值。然後在後續建立物件的時候,只引用這個常量物件的地址,而不對其再進行再次初始化。就如同Private static final int int2=rd1.nestInt這種形式來定義常量。如此,在後續多次建立物件後,這個常量int2的值都是相同的。因為在建立物件時,其只是引用這個常量,而不會對這個常量再次進行初始化。

由於加上這個static關鍵字之後,相當於改變了常量的作用範圍。為此程式開發人員需要了解自己的需求,然後選擇是否需要使用這個關鍵字。在初始化常量的時候,如果採用函式或者物件來初始化常量,可以預見到在每次初始化這個常量時可能得到不同的值,就需要考慮是否要採用這個static關鍵字。一般情況下,如果只需要保證在物件內部採用這個常量的話,那麼這個關鍵字就可有可無的。但是反過來,如果需要在多個物件中引用這個常量,並且需要其值相同,那麼就必須要採用static這個關鍵字了。以確保不同物件中都只有一個常量的值。或者說,不同物件中引用的常量其實指向的是記憶體中的同一塊區域。

5,科學計數法

6,除歐幾里得演算法外求兩數最大公因子--更相減損術

“更相減損術”可以用來求兩個數的 最大公約數

(如果需要對分數進行約分,那麼)可以折半的話,就折半(也就是用2來約分)。如果不可以折半的話,那麼就比較分母和分子的大小,用大數減去小數,互相減來減去,一直到減數與差相等為止,用這個相等的數字來約分。

方法步驟:

第一步:任意給定兩個正整數;判斷它們是否都是偶數。若是,則用2約簡;若不是則執行第二步。

第二步:以較大的數減較小的數,接著把所得的差與較小的數比較,並以大數減小數。繼續這個操作,直到所得的減數和差相等為止。

則第一步中約掉的若干個2與第二步中等數的乘積就是所求的最大公約數。

其中所說的“等數”,就是 最大公約數。求“等數”的辦法是“更相減損”法。

例1、用更相減損術求98與63的 最大公約數。

解:由於63不是偶數,把98和63以大數減小數,並 輾轉相減:

98-63=35

63-35=28

35-28=7

28-7=21

21-7=14

14-7=7

所以,98和63的最大公約數等於7。

例2、用更相減損術求260和104的最大公約數。

解:由於260和104均為偶數,首先用2約簡得到130和52,再用2約簡得到65和26。

此時65是奇數而26不是奇數,故把65和26 輾轉相減:

65-26=39

39-26=13

26-13=13

所以,260與104的 最大公約數等於13乘以第一步中約掉的兩個2,即13*2*2=52。

比較:

輾轉相除法也可以可以用來求兩個數的 最大公約數

更相減損術和輾轉相除法的主要區別在於前者所使用的運算是“減”,後者是“除”。從演算法思想上看,兩者並沒有本質上的區別,但是在計算過程中,如果遇到一個數很大,另一個數比較小的情況,可能要進行很多次減法才能達到一次 除法的效果,從而使得演算法的 時間複雜度退化為O(N),其中N是原先的兩個數中較大的一個。相比之下,輾轉相除法的時間複雜度穩定於O(logN)。

Stein演算法

更相減損法有點類似於求 最大公約數的 Stein演算法。在更相減損法中,若兩個是偶數則同除以2,結果乘以2。如果增加一個判斷,若為一奇一偶則偶數除以2,結果不變,若為兩個奇數才相減,這樣就變成了目前計算大整數最大公約數的非常好的一個演算法,Stein演算法

在上面的例項中,下面是更相減損法與Stein演算法的比較,從中可以發現兩種演算法的相似性。

更相減損法:操作

甲數

乙數

甲數

乙數

98

63

98

63

98-63=35

63

35

98是偶數,除以2

49

63

63-35=28

35

28

都是奇數,63-49=14

49

14

35-28=7

28

7

14是偶數,除以2

49

7

28-7=21

7

21

49-7=42

42

7

21-7=14

7

14

42是偶數,除以2

21

7

14-7=7

7

7

21-7=14

14

7

7-7=0

7

0

14是偶數,除以2

7

7

7-7=0

7

0

“可半者半之”

通常認為,演算法描述中的第一步“可半者半之”是指分子 分母皆為偶數的時候,首先用2約簡。因為更相減損術原先是專用來 約分,所以並不用考慮最後計算結果時,要把第一步中約掉的若干個2再乘回去。加入這一步的原因可能是,分母、分子皆為偶數是在分數加減運算的結果中比較容易遇到的一種情況,用這種方法有可能減少數字的位數,簡化計算。

當然,省略這個以2約簡的步驟,也能得到正確的答案

C語言

int gcd(int a, int b){
    while(a != b){        
        if(a > b)       
            a -= b;        
        else        
            b -= a;        
        }    
    return a;
}

7,生成java中的隨機數

生成指定範圍內的隨機數

這個是最常用的技術之一。程式設計師希望通過隨機數的方式來處理眾多的業務邏輯,測試過程中也希望通過隨機數的方式生成包含大量數字的測試用例。問題往往類似於:

如何隨機生成 1~100 之間的隨機數,取值包含邊界值 1 和 100。

或者是:

如何隨機生成隨機的3位整數?

等等……

以 Java 語言為例,我們觀察其 Random 物件的 nextInt(int) 方法,發現這個方法將生成 0 ~ 引數之間隨機取值的整數。例如(假設先有 Random rand = newRandom();,下同):

rand.nextInt(100);

這行程式碼將生成範圍0~100 之間的隨機數,有趣的是,取值可能為 0 ,但不可能為 100。我們用中學數學課學習的區間表示法,表示為:[0, 100)。

那麼如果要獲得區間 [1~100]的隨機數,該怎麼辦呢?稍微動動腦筋就可以想到:區間 [0, 100) 內的整數,實際上就是區間 [0, 99]。因為最大邊界為100,可惜不能等於100,因此最大可能產生的“整數”就是99。

既然rand.nextInt(100) 獲得的值是區間 [0, 99],那麼在這個區間左右各加 1,就得到了區間 [1, 100]。因此,程式碼寫成:

rand.nextInt(100)+ 1;

即可。執行下面的程式碼,將獲得 [1, 100] 的 10 個取值。

import java.util.Random;
public class Test {
    public static void main(String[] args){
      Random rand = new Random();
      for(int i=0; i<10; i++) {
       System.out.println(rand.nextInt(100) + 1);
      }
    }
}

同理,很容易知道如果要獲得隨機兩位整數,程式碼寫成:rand.nextInt(90) + 10;

你一定很驚訝,為什麼是這麼寫出來的。其實,在 nextInt() 方法中作為引數的數字 90 表示:你希望生成的隨機數的所有取值的可能性的數量(在本命題中,兩位整數取值為 [10, 99],共90個數);加好後面的數字 10 ,表示區間的最小取值。

你可以驗證下,按照這樣理解,[1, 100] 的隨機數,是不是應該寫成rand.nextInt(100) + 1 。千萬不要把引數 100 理解為最大取值。只是區間 [1, 100] 正好從 1 開始,所以最大取值和取值可能性數量正好同為 100。

因此,

生成隨機三位數的程式碼為:

rand.nextInt(900)+ 100;

生成區間 [64,128] 中隨機值的程式碼為:

rand.nextInt(65)+ 64;

取值可能性的數量是如何計算出來的呢?當然是 最大取值-最小取值+1 ,所以,有最終公式如下

// For Java

int randNumber =rand.nextInt(MAX - MIN + 1) + MIN; // randNumber 將被賦值為一個 MIN 和 MAX 範圍內的隨機數