201521123091 《Java程序设计》第2周学习总结
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();
}
}
下面贴一下最后的运行结果
看的不过瘾的请点下面
回到顶部
最后总是要说句废话什么的,有同学和我说我的排版太丑陋了。嗯……我只能顾上眼前的苟且了,非常感谢偷偷看我博客的同学,可是能不能粉我一下,我也会回粉你们的,谢谢~