Java 第二周總結
第二周的作業。
一個簡陋的目錄
1.本章學習總結
2.Java Q&A
3.使用碼雲管理Java代碼
4.PTA實驗
5.小任務
1.本章學習總結
基本數據類型
- String類
- Java的標准輸入輸出和文件輸入輸出
- Java控制執行流程
- Java數組的使用
- 類管理機制:包
2.Java Q&A
1.使用Eclipse關聯jdk源代碼(截圖),並查看String對象的源代碼?簡單分析String對象的設計思路。
- 從“Window (菜單項目)”上選擇“Preferences (菜單項目)”
- 展開“Java (框線項目)”(位於“Preferences”中)
- 選擇“Installed JREs (框線項目)”(位於“Preferences”中)
- 選中當前JRE,並且點擊Edit按鈕
- 展開JRE系統庫的第二項,選中展開后的第一項,點擊“Source Attachment”按鈕
- 打開JDK目錄,選中src.zip,最后一路確定出去
- ctrl+鼠標左鍵選中某一個類,或者選中類之后按F3,源代碼文件彈出,第一問就搞定了
到此為止,只是這一問的第一小問結束了0.0(各位看官不急,我還在碼)
String對象的設計思路(我們可以看到在源代碼文檔里面已經有說了,所以我們要做的僅僅是翻譯一下,當然英文不好的同學(比如我這種),可以借助類似金山詞霸之類的划譯)
我們可以看到就是這點,字符串是常量,往下翻翻我們就會看到private final char value[];
,用一個final修飾的字符數組來實現這個字符串的
2.為什么要盡量頻繁的對字符串的修改操作應該是用StringBuilder而不是String?
這個東西我們就有講頭了,不妨來看幾個String類自帶的方法吧。
//這是一個字符串連接的方法
public String concat(String str) {
int otherLen = str.length();
if (otherLen == 0) {
return this;
}
int len = value.length;
char buf[] = Arrays.copyOf(value, len + otherLen);
str.getChars(buf, len);
return new String(buf, true);
}
//這是一個替換的方法
public String replace(char oldChar, char newChar) {
if (oldChar != newChar) {
int len = value.length;
int i = -1;
char[] val = value; /* avoid getfield opcode */
while (++i < len) {
if (val[i] == oldChar) {
break;
}
}
if (i < len) {
char buf[] = new char[len];
for (int j = 0; j < i; j++) {
buf[j] = val[j];
}
while (i < len) {
char c = val[i];
buf[i] = (c == oldChar) ? newChar : c;
i++;
}
return new String(buf, true);
}
}
return this;
}
在對字符串進行相應修改(例如替換或者是連接)的時候,我們可以發現最后都返回了一個新的字符串常量。所以我們可以想見,如果有大量的字符串拼接的話,那么我們的代碼一定會new出很多的String變量,然后又會有很多的垃圾回收。這樣的話,代碼的效率就會有所降低。
這邊來看個代碼吧(當然是從Thinking in Java上抄下來的。。。)
public class Concatenation{
public static void main(String[] args) {
String mango = "mango";
String s = "abc" + mango + "def" + 47;
System.out.println(s);
}
}
然后使用javap進行反匯編看看發生了什么。。。
Compiled from "Concatenation.java"
public class Concatenation {
public Concatenation();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: ldc #2 // String mango
2: astore_1
3: new #3 // class java/lang/StringBuilder
6: dup
7: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
10: ldc #5 // String abc
12: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/Stri
ngBuilder;
15: aload_1
16: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/Stri
ngBuilder;
19: ldc #7 // String def
21: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/Stri
ngBuilder;
24: bipush 47
26: invokevirtual #8 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
29: invokevirtual #9 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
32: astore_2
33: getstatic #10 // Field java/lang/System.out:Ljava/io/PrintStream;
36: aload_2
37: invokevirtual #11 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
40: return
}
好了,我們看到這個編譯器自動引入了一個叫做StringBuilder的東西,,使用它來進行字符串的拼接。這當然是因為StringBuilder更好了,我們剛才說String是immutable的,那這個StringBuilder是專門為字符串修改而生的,在Java SE5引入。所以編譯器很聰明的對代碼進行了優化。那也就是說,我可以隨意使用String,等着代碼優化嗎?當然不行咯,Java編程思想又給我們舉了個反例,來看看。
哦,對了,在String的源文檔里面有這樣一句話,一樣的意思
String concatenation is implemented through the {@code StringBuilder}(or {@code StringBuffer}) class and its {@code append} method.
利用循環來進行字符串的拼接
//輸入一個n,從0-n的數字轉成字符串進行拼接
public class Main{
public static void main(String[] args) {
int n = 5;
String result1 = "";
for (int i = 0; i < n; i++) {
result1 += i;
}
System.out.println(result1);
StringBuilder result2 = new StringBuilder();
for (int i = 0; i < n; i++) {
result2.append(i);
}
System.out.println(result2.toString());
}
}
我們再來反匯編(* * *大法好,雖然我不是很懂)
Compiled from "Main.java"
public class Main {
public Main();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: iconst_5
1: istore_1
2: ldc #2 // String
4: astore_2
5: iconst_0
6: istore_3
7: iload_3
8: iload_1
9: if_icmpge 37
12: new #3 // class java/lang/StringBuilder
15: dup
16: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
19: aload_2
20: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/Stri
ngBuilder;
23: iload_3
24: invokevirtual #6 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
27: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
30: astore_2
31: iinc 3, 1
34: goto 7
37: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream;
40: aload_2
41: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
44: new #3 // class java/lang/StringBuilder
47: dup
48: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
51: astore_3
52: iconst_0
53: istore 4
55: iload 4
57: iload_1
58: if_icmpge 74
61: aload_3
62: iload 4
64: invokevirtual #6 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
67: pop
68: iinc 4, 1
71: goto 55
74: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream;
77: aload_3
78: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
81: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
84: return
}
代碼中9-34行是第一個循環(用String的那個),我們可以看到,每次循環開始都會new一個StringBuilder變量,58-71行是第二個循環(用StringBuilder的那個),當然是只在我們一開始new了一個。這個循環更簡短,效率當然更高。
3.比較兩個字符串的值是否相等?為什么不能用==直接進行比較?
因為用進行比較並不能保證結果總是正確的(假設下面的情況字符串的內容都是相同的),比較的是兩個對象的引用
-
首先如果兩個字符串都是以
String xxx = "xxxxx";
的方式創建出來的,在第一個字符串如此創建時,JVM就會在字符串池中維護這么一個String的實例,那么以后只要有內容完全相同的字符串,就直接指向這個實例,所以引用會相同 -
只要有一個是以new的方式創建新的實例,就會創建一個新的對象,當然就有不一樣的引用,所以這時候==的兩邊是不同的引用,當然會返回false
綜上所述,我們要對兩個字符串進行內容的比較的話,我們要選用一種不是==的方法,那就是equals()方法,看下面的具體實現,沒毛病
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
但是不是所有的equals()方法都是比較內容,我們這邊可以舉個小小的反例
class Value{
int i;
}
public class EqualsMethod2 {
public static void main(String[] args) {
Value v1 = new Value();
Value v2 = new Value();
v1.i = v2.i = 100;
System.out.println(v1.equals(v2));
}
}
輸出結果是false,這是因為equals()的默認行為是比較引用,只是Java類庫當中的許多類都已經自己實現了equals()方法,顯然我們這邊並沒有重寫這個equals()的方法,所以這邊還是只是比較引用
4.嘗試使用字符串池的概念解釋如下程序段輸出結果,並回答這段代碼創建了幾個字符串對象:
String str1 ="hi", str2="hi";
String str3 = new String(str1);
System.out.println(str1==str2);
最后輸出true
分析在前面已經說過了,一共會有兩個對象,str1創建的時候,字符串池里面會有一個,也就是str2直接指向字符串池中的那個實例。
然后str3在字符串池外面又new了個
5.Integer i = 100;//100是基本類型,i是引用類型,為什么可以將100賦值給i
public class Main {
public Main();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: bipush 100
2: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
5: astore_1
6: return
}
同樣看到反匯編,我們就可以看到只是調用了Integer類的valueOf的方法而已,然后這個方法是public static Integer valueOf(int i)
這樣子的,返回一個Integer
6.嘗試分析下面代碼輸出結果
Integer i1 = 127;Integer i2 = 127;
i1 == i2;//true of false?
Integer i1 = 128;Integer i2 = 128;
i1 == i2;//true of false
輸出結果分別是true和false
上回我們剛剛談到,我們用到了valueOf()這個方法,去給這個Integer進行賦值,so我們需要研究一下這個方法,貼上JDK源碼。
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
!這是什么東西,對於不同的范圍還區別對待?
是這樣的,這個IntegerCache,整數緩存是?再貼源代碼(截取部分)
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
好了,這樣我們就知道了,這個IntegerCache是通過Cache數組來實現的,然后-128-127這256個數就被放在了這個數組里,當我valueOf()方法的參數在這個范圍內的話,我就將數組中的值返回去,也就是說一開始i1和i2都指向了數組的同一位置,所以當然是相等的咯
然后這個128就相當的尷尬了,因為他正好就在這個范圍之外了,所以我們用最朴素的方法創建這個Integer對象,那么就像我們之前所說,通過new來創建的對象具有不同的引用,so傳回了false
7.package與javac、 java、 -classpath、 -d
在 com.ibm包中編寫 StringUtil類,內有一方法
public static void foo1(){
System.out.println(StringUtil.class+" method:foo1");
}
- 嘗試用命令行進行編譯並運行,截圖使用
javac -d. StringUtil.java
使用-d編譯選項,建立了相應的文件,將生成的字節碼放在了\com\ibm下面
編寫Main.java,在第一行加上package edu.jmu
,編譯后再次生成相應字節碼文件
然后再用java命令執行,結果如截圖
- 將生成的StringUtil.class放到d:\lib正確的目錄結構下,將Main.class在d:\test正確的目錄結構,嘗試在命令行下運行,並截圖。
目錄結構如下所示:- D
- lib
- com
- ibm
- StringUtil.class
- ibm
- com
- test
- edu
- jmu
- Main.class
- jmu
- edu
- lib
- D
之前用powershell搞了很久,懟不出來。最后換成cmder就成功了
- Eclipse中源代碼放在哪個目錄、class文件放在哪個目錄。在Eclipse項目中按一下ctrl+f11就可以直接運行Main,嘗試分析背后實現的原理
源代碼放在src里面,class放在bin文件夾中。
8.自己在這門課的目標與計划
-
請描述一下你的技術基礎(會什么語言,都寫了多少行代碼)
- C 有上萬嗎,可能有,可能沒有吧
- Matlab 上一千行,應該是有了
- Python 上百行
-
一周准備花多少時間在這門課上?一周准備寫多少行代碼?采用怎樣的學習方式?遇到困難打算怎樣解決?
- 具體多長時間不清楚,畢竟還得顧着我的藍橋杯
- 一周寫個一百多行什么的,還是可以的吧,具體的代碼量同樣還是不清楚
- 遇到困難
- 百度、谷歌……
- 看書
- 問老師
放棄,我不搞了,開玩笑的
-
關於這門課的smart目標
首先這個是SMART
S pecific:具體的,無二義性的,能描述 “成功” 是什么樣的。
M otivating: 目標能激發對目標的興趣么?實現目標對學生來說說意味着什么?他們會為之自豪么?
A chievable: 能做到么?是挾泰山以超北海?還是把牆角一堆磚頭搬走?
R elevant: 和學生來到大學的大方向、目標吻合
T rackable: 能衡量進度的,和有些資料提到的 Measurable 相似。
要不就姑且試試Java web?雖然別人說這都是套框架,可是我連框架都不會套……
這讓我想到職業規划的SWOT(S trengths, W eaknesses, O pportunities, T hreats)。那我就分門別類的說下吧。
- 我覺得對於一門語言來說,怎么算是學成功了呢,應該就是能夠使用這門語言真的做一件有實際用途的事情,可能主要是做開發之類的吧(現在還不是很清楚)
- 分情況,假如我剛開始學編程的時候,會打printf("%d", a + b);我都會很開心。然而我們不可能總是停留在這個層次,我們總是需要向上走
- 至於可行性,如果暫且把目標放的低一點,做一些簡單功能的實現,我覺得還是可以的吧,不能一口吃成一個大胖子,也不能推得太慢吧。
- 沒什么大方向,也沒什么具體的目標吧。很慚愧,感覺還是把現在的事情做好了再說。
- 沒試過啊,進度什么的先放着吧,要是凡事都能有個進度條就好了,很可惜並不是
話再說回來,很想在這個學期繼續打牢計算機以及數學基礎。不然就可以直接去培訓機構,三個月上崗,而不用來大學了
9.選做:公交卡里應該還有多少錢?請分析原因
-0.1(元)參考騰訊新聞
可能是無符號浮點數下溢了。
不過……
新聞里說是這台閘機的問題,所以其他的閘機肯定是能正常顯示的,那至於這個為什么不對,就不清楚了。
3.使用碼雲管理Java代碼
4.PTA實驗
- 第一題,是函數調用,就是要注意需要sort的數組需要在循環輸入選項的代碼塊外面就創建,如果在sort數組里面臨時創建,當然其它選項是找不到的啦
- 第二題,StringBuilder類的基本使用,就是一開始的時間限制壓得很死,所以一會能A,一會又不能A。
- 第三題,就是要自己去實現一下Comparator,然后就很流暢了
- 第四題,for循環也可以,就是要一個二維數組,因為數據量不是很大,所以我是直接初始化了
- 第五題,使用BigDecimal類來實現浮點數的精准計算,當然也可以用數組的方式來實現。不過既然有現成的,就用現成的好了
- 第六題,枚舉的基本使用
- 第七題,可以用BigInteger實現的東西,就是學會用
- 第八題,ArrayList的基本使用,相當於數據結構中提到的鏈表
5.小任務
代碼有點丑陋……不過效果還行吧
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.util.Scanner;
public class Main {
//用來找第一個不是空格的字符
private static int firstCharNotSpace(String string) {
int i;
for (i = 0; i < string.length(); i++) {
if (string.charAt(i) != ' ') {
break;
}
}
return i;
}
//如果是ABCD或是數字啥的,那么前面有空格就要刪
private static Boolean needToDeleteSpace(char ch) {
if (Character.isDigit(ch) || ch >= 'A' && ch <= 'D') {
return true;
}
return false;
}
public static void main(String[] args) throws FileNotFoundException {
Scanner sc = new Scanner(new File("C:/Users/LJL36/Desktop/choice.txt"));
PrintWriter printWriter = new PrintWriter("C:/Users/LJL36/Desktop/output.txt");
int res = 0;//記錄有多少道題目
//這邊很蠢的先統計了一下題目
while (sc.hasNextLine()) {
String string = sc.nextLine();
if (string.trim().length() == 0) {
continue;
}
int index = firstCharNotSpace(string);
if (Character.isDigit(string.charAt(index))) {
res++;
}
}
printWriter.println(res);
//重新打開文件
sc = new Scanner(new File("C:/Users/LJL36/Desktop/choice.txt"));
while(sc.hasNextLine()){
StringBuilder stringBuilder = new StringBuilder(sc.nextLine());
String string = stringBuilder.toString();
//如果是空格串,就跳過
if (string.trim().length() == 0) {
continue;
}
//刪空格
int index = firstCharNotSpace(string);
if (needToDeleteSpace(string.charAt(index))) {
stringBuilder.delete(0, index);
}
//將答案在后面用pta要求格式輸出
index = string.lastIndexOf('。');
if (index != -1 && index != string.length()) {
boolean flag = false;
String string2 = "@[";
for (int i = index + 1; i < string.length(); i++) {
if (Character.isLetter(string.charAt(i))) {
flag = true;
string2 += Character.toUpperCase(string.charAt(i));
}
}
string2 += "](2)";
stringBuilder.delete(index + 1, string.length());
if (flag) {
stringBuilder.append(string2);
}
}
stringBuilder.append(" ");
printWriter.println(stringBuilder.toString());
}
sc.close();
printWriter.close();
}
}
下面貼一下最后的運行結果
看的不過癮的請點下面
回到頂部
最后總是要說句廢話什么的,有同學和我說我的排版太丑陋了。嗯……我只能顧上眼前的苟且了,非常感謝偷偷看我博客的同學,可是能不能粉我一下,我也會回粉你們的,謝謝~