2021-06-27


哈工大软件构造阅读心得2-1: Java基础


本文参考:MIT Reading2哈工大学长汉化
注:这个系列是本人看过阅读资料之后,对看过的内容进行的总结

练习

基础

下面这块代码取自某一个函数中:
i

nt a = 5;     	// (1)
if (a > 10) {  	// (2)
int b = 2; 		// (3)
} else {       	// (4)
int b = 4;		// (5)
}               // (6)
b *= 3;        	// (7)

哪一行会导致编译时报错?
7
在这里插入图片描述
报错原因:
b cannot be resolved to a variable.Java(33554515)

修改bug

选择出最简单能改掉这个bug的操作:
[x] 在第一行后声明 int b;
[ ] 在第二行之前赋值 b = 0;
[ ] 第三行改为 b = 2;
[ ] 第五行改为 b = 4;
[ ] 第七行改为声明并赋值 int b *= 3;
我们按照上面的修改策略进行了修改,如果我们将else块注释掉,会发生什么呢?
[ ] b 为 0
[ ] b 为 3
[ ] b 为 6
[x] 编译器会报错,在我们运行程序之前
[ ] 在我们运行程序的时候报错,在我们到达最后一行之前
[ ] 在我们运行程序的时候报错,在我们到达最后一行的时候

数字与字符串

下面这个程序语句将华氏温度转化为摄氏温度
fahrenheit = 212.0
celsius = (fahrenheit - 32) * 5/9
如果改用Java写,第一行应该改为
[ ] int fahrenheit = 212.0;
[ ] Integer fahrenheit = 212.0;
[ ] float fahrenheit = 212.0;
[ ] Float fahrenheit = 212.0;
[x] double fahrenheit = 212.0;
[x] Double fahrenheit = 212.0;
第二行应该改为(???是你上面选择的类型):
[x] ??? celsius = (fahrenheit - 32) * 5/9;
[ ] ??? celsius = (fahrenheit - 32) * (5 / 9);*
[x] ??? celsius = (fahrenheit - 32) * (5. / 9);**
应该如何输出?
[ ] System.out.println(fahrenheit, " -> “, celsius);
[x] System.out.println(fahrenheit + " -> " + celsius);
[ ] System.out.println(”%s -> %s" % (fahrenheit, celsius));
[x] System.out.println(Double.toString(fahrenheit)+" -> " + Double.toString(celsius));

用快照图理解值与对象

快照图:代表程序运行时的各种状态——它的栈(即方法和局部变量)和它的堆(即现在存在的对象)。

原始值

在这里插入图片描述

原始值都是以常量来表达的。上面箭头的来源可以是一个变量或者一个对象的内部区域(field)。

对象值

在这里插入图片描述

一个对象用一个圆圈表示。对象内部会有很多区域(field),这些区域又指向它们对应的值。同时这些区域也是有它们的类型的,例如int x 。
可更改的值 vs. 可被重新赋值的改变
通过快照图我们可以视图化可更改的值和可被重新赋值的改变之间的区别:
当你给一个变量或者一个区域(filed)赋值的时候,你实际上是改变了它指向的方向,即指向了另一个值。
当你修改一个可被更改的(mutable)值的时候——例如数组或者列表——你真正修改了这个值本身(译者注:变量或者区域的指向并没有变)
在这里插入图片描述

例如,如果我们有一个 String 变量 s, 我们可以将它从 “a” 赋值为 “ab”.
String s = “a”;
s = s + “b”;
String 就是一种不可改变的(immutable)值,这种类型的值在第一次确定后就不能改变。
不可更改的对象(设计者希望它们一直是这个值)在快照图中以双圆圈的边框表示,例如上面的字符串对象。

可更改的(mutable)值

与此相对应的, StringBuilder (Java的一个内置类) 是一个可更改的字符串对象,它内置了许多改变其内容的方法:
StringBuilder sb = new StringBuilder(“a”);
sb.append(“b”);
在这里插入图片描述

可更改性和不可更改性(mutability and immutability)将会对我们的“安全健壮性”目标起到重要作用。

不可更改的引用

Java也提供了不可更改的引用:final声明,变量一旦被赋值就不能再次改变它的引用(指向的值或者对象)。
final int n = 5;
如果Java编译器发现final声明的变量在运行中被赋值多次,它就会报错。所以final就是为不可更改的引用提供了静态检查。
在快照图中,不可更改的引用(final)用双箭头表示,例如下图中的id,Person的id引用不可改变,但是age却是可改变的。
在这里插入图片描述

这里要特别注意一点,final只是限定了引用不可变,我们可以将其引用到一个可更改的值 (例如final StringBuilder sb ),虽然引用不变,但引用的对象本身的内容可以改变。
同样的,我们也可以将一个可更改的引用作用到一个不可更改的值(例如String s ),这个时候变量值的改变就是将引用改变。

Java 聚合类型

列表、集合、映射(Lists, Sets, and Maps)

列表可以包含零个或多个对象,而且对象可以出现多次。我们可以在列表中删除或添加元素。
一些 List 常见的操作:
在这里插入图片描述

在快照图中,我们用数字索引表示列表中的各个区域(filed),例如一个全是String对象的列表:
在这里插入图片描述

映射Map是一个存储关键字和值的关联,或者说是“关键字/值”对的对象,即给定一个关键字,可以得到它的值。关键字和值都是对象,关键字必须是唯一的,但是可以存在相同的值。
常用的 Map操作 :
在这里插入图片描述
在这里插入图片描述

在快照图中,我们将Map表示为包含key/value对的对象。例如一个Map<String, Turtle> :
在这里插入图片描述

集合是一种含有零个或多个不重复对象的聚合类型 。
常用的 Set 操作:
在这里插入图片描述

在快照图中,我们不用数字索引表示集合的元素(即元素没有顺序的概念),例如一个含有整数的集合:
在这里插入图片描述

List, Set, and Map的普遍声明方法

Java只为数组提供了类似的创建方法:
String[] arr = { “a”, “b”, “c” };
我们可以用the utility function Arrays.asList 从数组创建列表:
Arrays.asList(new String[] { “a”, “b”, “c” })
… 或者直接提供元素:
Arrays.asList(“a”, “b”, “c”)
要注意的是,如果一个 List 是用 Arrays.asList 创建的,它的长度就固定了。
在Java中,我们能够要求编译器对操作进行静态检查,确保聚合类中的元素类型相同。例如:
List cities; // a List of Strings
Set numbers; // a Set of Integers
Map<String,Turtle> turtles; // a Map with String keys and Turtle values
由于Java要求元素的普遍性,我们不能直接使用原始类型作为元素的类型,例如Set ,但是,正如前面所提到的, int有一个对应的 Integer ”包装“对象类型,我们可以用 Set numbers.
为了使用方便,Java会自动在原始类型和包装过的对象类型中做一些转换,所以如果我们声明一个 List sequence ,下面的这个代码也能够正常运行:
sequence.add(5); // add 5 to the sequence
int second = sequence.get(1); // get the second element

创建列表:ArrayList 与 LinkedList

我们马上就可以看到,Java区分了两个概念:类型的规格说明——它的行为;类型的实现——代码是是什么。
List, Set, 和 Map都是接口 :他们定义了类型的工作,但是他们不提供具体的实现代码。
例如List的创建:

List<String> firstNames = new ArrayList<String>();
List<String> lastNames = new LinkedList<String>();

如果左右两边的类型参数都是一样的,Java可以自动识别,这样可以少打一些字:

List<String> firstNames = new ArrayList<>();
List<String> lastNames = new LinkedList<>();

ArrayList 和 LinkedList 是实现List的其中两种方法。他们都提供了List要求的操作,而且这些操作的行为必须和文档规定的相同。在上面的例子中, firstNames 和 lastNames 的行为一样,也就是说,如果我们在一串代码中将 ArrayList vs. LinkedList互换,代码依然能够正常工作。至于什么选用哪一个作为创建链表的方式呢。由于这门课涉及不到的一些知识,这两个接口会对于程序性能的有不同的影响,当你不确定时,使用ArrayList 。

创建集合和映射:HashSets 与 HashMaps

对于集合,我们默认使用HashSet :

Set<Integer> numbers = new HashSet<>();

对于映射,我们默认使用 HashMap:

Map<String,Turtle> turtles = new HashMap<>();

迭代

我们创建了以下聚合类变量:

List<String> cities = new ArrayList<>();
Set<Integer> numbers = new HashSet<>();
Map<String,Turtle> turtles = new HashMap<>();

一个常见的工作就是遍历这些聚合类中的各个元素。
对于List 和 Set ,Java提供了类似的语法:

for (String city : cities) {
    System.out.println(city);
}
for (int num : numbers) {
    System.out.println(num);
}

我们不能对Map进行完全一样的操作,但是我们可以像上面那样遍历它的keys,结合映射对象提供的方法来遍历所有的对(pairs):

for (String key : turtles.keySet()) {
    System.out.println(key + ": " + turtles.get(key));
}

实际上,这个for循环用到了 Iterator。
警告: 一定要注意在循环的时候不要改变你的循环参量(他是可改变的值)!添加、删除、或者替换都会影响你的循环甚至中断你的程序。
使用数字索引进行迭代
Java也提供了一种使用数字索引进行迭代的方法(译者注:C的标准写法):

for (int ii = 0; ii < cities.size(); ii++) {
    System.out.println(cities.get(ii));
}

除非你真的需要索引ii,否则我们不推荐这种写法,它可能会引来一些难以发现的bug。

练习

聚合类型

将下面使用数组声明的变量用List进行声明(不用初始化):
在这里插入图片描述

“找出关键点”

在运行下列代码后:

Map<String, Double> treasures = new HashMap<>();
String x = "palm";
treasures.put("beach", 25.);
treasures.put("palm", 50.);
treasures.put("cove", 75.);
treasures.put("x", 100.);
treasures.put("palm", treasures.get("palm") + treasures.size());
treasures.remove("beach");
double found = 0;
for (double treasure : treasures.values()) {
    found += treasure;
}

以下操作之后变量的值分别为:
treasures.get(x) -> 54.0
treasures.get(“x”) -> 100.0
found -> 229.0

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值