校历第三周

[TOC]

TODO

Taps

微信小程序:setData()

原理案例

// 方法一:先准备好新的数组对象,再去赋值
let fm_ = this.data.fm;
fm_[1] = "方法 一";
let fs_ = this.data.fs;
fs_[1] = "方法 一";
this.setData({
fm: fm_,
fs: fs_,
});
// 方法二:先准备好新的Key对象,再去赋值
// 注意,Index 值 必须要用 () 括号运算,保证字符串中的值是 数字
let setFm = "fm[" + (this.data.nowDifficulty - 3) + "]";
let setFs = "fs[" + (this.data.nowDifficulty - 3) + "]";
this.setData({
[setFm]: "方法 二",
[setFs]: "方法 二",
});
// 方法三:方法二的另一种样子
this.setData({
["fm[2]"]: "方法 三",
["fs[2]"]: "方法 三",
});
// 在调试器中打印数据
console.log(this.data);
console.log("fm", this.data.fm);
console.log("fs", this.data.fs);

实际案例

// 配套的 wxml
// <view class="v5">历史最快记录:{{fm[nowDifficulty-3]}}:{{fs[nowDifficulty-3]}}</view>
let fm_path = "fm[" + (this.data.nowDifficulty - 3) + "]";
let fs_path = "fm[" + (this.data.nowDifficulty - 3) + "]";
let fm_val;
let fs_val;
if (
parseInt(this.data.fm[this.data.nowDifficulty - 3]) * 60 +
parseInt(this.data.fs[this.data.nowDifficulty - 3]) >=
parseInt(m) * 60 + parseInt(s)
) {
fm_val = m;
fs_val = s;
} else {
fm_val = fm[this.data.nowDifficulty - 3];
fs_val = fs[this.data.nowDifficulty - 3];
}
this.setData({
//存下最快纪录
[fm_path]: fm_val,
[fs_path]: fs_val,
});

课程记录

周一

大数据可视化

  • 应该从正面选择,而不是从负面来选择
    • 例如:
    • 应该是因为想做某件事,而去做
    • 不应该是因为不想做另一件事然后选择的这件事
  • OFFICE企业版—— 就可以加装 Microsoft Visio
  • 数据清洗:有些位置的数据,如果是异常高(999999)或者未收集到(异常低,零),就需要用一段时间内的平均值代替,这就是数据清洗
  • 任务四步:
    • 数据收集
    • 数据清洗
    • 数据观察
    • 结论提交
  • 小数据时代,保证数据准确的最重要的方法是,随机性
  • 最早的电子表格软件:Lotus 123
  • EXCEL的进阶使用: 的打开,记录,修改

周二

Java高级

TOC:

  • Java集合框架层次结构中,接口和类的关系
  • Collection接口中定义的通用方法
  • Iterator遍历能力
  • ForEach for( var x : ... )循环,forEach() 函数
  • Comparable接口之外,Comparator接口的比较能力
  • Collections中的静态工具方法

不想让子类使用的方法

如果一个方法在子类中没有意义,可以按如下方式实现它:

public void someMethod(){
throw new UnsupportedOperationException("Method not supported");
}

Iterator 接口

  • Collection 接口是 继承Iterable 接口的
  • Iterable 接口,定义了 iterator() ,这个方法会返回一个 Iterator 实例迭代器
  • .next() 顺序访问下一个
  • .hasNext() 检测是否还有下一个
  • .remove() 移除迭代器返回的最后一个元素
  • .forEach()
    • 可以接受lambda表达式作为参数
    • 本身实际的参数类型是,函数接口 Consumer<? Super E> 的一个实例
// 两者等价
// lambda
_.forEach( e -> {System.out.print(e.toUpperCase);} );
// 匿名内部类
_.forEach( new java.util.function.consumer<String>(){
public void accept(String e){
System.out.print(e.toUpperCase);
}
})

Collection 接口

  • .addAll() .removeAll() .retainAll() 分别是 并 差 交 运算
  • .size() 返回集合中元素的个数
  • .containsAll() 检测这个集合是否包含指定集合中的全部元素
  • toArray() 返回一个 Object数组
  • toArray(T[]) 返回一个 T[]数组

List 接口

  • List 接口 是继承Collection接口
  • List 接口 增加了面向位置的操作
  • List 接口 增加了一个能双向遍历线性表的新线性表迭代器.listIterator()
  • ListIterator 实例 增加了三个方法:.hasPrevious() .previous() .previousIndex()

LinkedList & ArrayList & Vector & Stack

  • LinkedListList接口链表实现
  • LinkedList 提供从线性表两端的 获取插入删除 元素的方法
  • LinkedList 的遍历应避免使用 .get(index) ,这效率极低
  • LinkedList遍历应使用 for( var k : ... )_.forEach(...),来隐式使用迭代器
  • ArrayListList接口 的数组实现
    • 这个内置数组自动扩容
    • 但是不会自动缩容
    • .trimToSize() 主动缩容至线性表大小
  • VectorArrayList 完全类似,唯一的不同是,Vector 的方法都是同步方法
  • 如果不需要同步,最好使用 ArrayList,因为它比 Vector 快得多
  • Stack继承Vector 的扩展
  • Vector() 的一些方法:
    • .capacity() 返回该向量的当前容量
    • .ensureCapacity() 增加该向量的容量
    • .copyInto(anArray: Object[]):void 将该向量的元素复制到数组中
    • .elements():Enumeration<E> 返回该向量的一个枚举
    • .trimToSize() 裁剪该向量的容量到其大小
  • Stack的方法:.search(o1) 返回该栈中指定元素的位置

Comparator 接口

  • Comparator 接口 可以用于比较没有实现 Comparable 接口的类的对象
  • Comparator 接口 可以用于定义比较对象的新标准
  • Comparator 接口 包含方法
    • .compare(o1,o2)
    • .comparing( func )
    • .thenComparing( func )
    • .reverse() 用于生成逆序比较器
  • .comparing( func ).thenComparing( func ) 在编写简单的基于属性简单比较逻辑时极度方便好用
Arrays.sort(list,Comparator.comparing(MyClass::getMainVal).thenComparing(MyClass::getOtherVal));

Collections 类 静态方法

  • 。。。部分我认为简单常见的不再列出
  • reverseOrder() 返回一个逆序排序的比较器
  • shuffle(c1) 随机打乱指定的线性表
  • disjoint(c1,c2) 如果 c1c2 没有任何共同的元素,则返回
  • copy(des,src) 浅拷贝复制了元素的引用

Queue 接口: Deque & PriorityQueue

  • Queue 接口继承自 java.util.Collection
  • Queue 接口加入了 插入提取,检验 等操作
  • Queue 定义的方法:
    • offer(e) 元素 e 入队
    • 元素出队并获取元素
      • poll() 队空则抛出null
      • remove() 队空则抛出异常
    • 获取队头元素且不出队
      • peek() 队空则抛出null
      • element() 队空则抛出异常
  • Deque接口 继承自 Queue接口LinkedList类 实现了 Deque接口
  • 因此,可以用 LinkedList 来创建一个**双端队列**
  • LinkedList 很适合用于队列操作,可以高效在列表的两端插入或移除
  • Deque接口 继承自 Queue接口DequeDouble-Ended Queue双端队列
  • Deque增加了队列从两端插入和删除元素的方法
    • .addFirst(e) .getFirst(e) .removeFirst(e)
    • .addLast() .getLast() .removeLast()
  • PriorityQueue类 实现了一个优先队列
  • 默认情况下,优先队列使用 Comparable 以元素的自然顺序进行排序
  • 拥有最小数值的元素被赋予最高优先级(也就是,将最先出队
  • 如果几个元素拥有相同的最高优先级,则任意选择一个
  • 可以使用构造方法 PriorityQueue( initialCapacity, comparator ) 中的 comparator 来指定一个顺序

周三

软工导论

  • 社会内卷

    • 一开始大家都在努力
    • 后来,其中一个人通过努力淘汰了另一个人
    • 不过人是会老的,而社会的节奏氛围快起来就慢不下来了
    • 于是最后两个人都被淘汰了
  • 作者:简在我心
    链接:https://www.zhihu.com/question/346195467/answer/1287373146
    来源:知乎
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    一句话,大家越来越努力,越来越累,但是收获并没有增加,甚至实际上是下降了。

    就像高考和高校绩效考核。

    第一年,大家每年发一篇论文能得一万。

    后来学校说多发多得。于是有人一年发了两篇,得了两万。

    第二年,所有人发了两篇,结果大家还是只得了一万。因为学校的钱是个定数,没有增加。就像清北每年招生人数没有增加,增加的是所有人的努力。

    第三年所有人发了三篇,所有人还是只得了一万……

    n年后,大家每年发n篇论文,年终分红还是1万。

    高考也是如此。

    第一年,大家学习八小时,有个别同学学了十小时,他考上了清北。

    第二年大家都学十个小时,结果只有学了十二个小时的学生考上了清北。

    几年后,所有人每天学二十四个小时,清北还是只招那么千把人,怎么办呢?看谁家钱多,请的家教更牛逼……

    这让我想起一部小说,属于内卷的升级版本。

    刘震云写过一部小说叫《故乡相处流传》,里面有个吃蛋糕的情节。

    大版食堂,把村里所有粮食做成一个大蛋糕,一开始随便吃。

    后来看到蛋糕小了,村民意识到以后要挨饿了,于是拼命抢。

    可是越是拼命抢,蛋糕消失得越快,所有人饿死得更快。

    可是不抢的话,你饿死得更早。

    是不是有点眼熟?

    你不努力,你坠落更快;可是大家努力,大家坠落得更快。

    ———

    原来一群人过独木桥,大家都慢悠悠的,每天能过去一千个人。

    后来大家都聪明了,纷纷抢着过,结果堵在桥上,速度反而慢了。

    再挤下去,独木桥断了。

    还记得以前挤火车吗?

    你不挤,你就没有机会上车。

    可是大家都挤的话,因为有人的包被卡在门口,谁也进不了,越挤越动不了。

    于是火车就开不了。

    内卷,给人一种恐慌的感觉,大家在恐惧的驱使下,竭尽所能争夺资源,可是资源有限,大家越是争夺,资源损坏越大,甚至无序的竞争会带来毁灭性结局。

    可是你不争,就被别人得了便宜,被人骂沙币。

    归根结底,这样的争抢式的努力,只是从别人身上挖肉,而不是创造出新的价值。

    这样的努力,看着很感人,实质上是无奈的徒劳的。

    ————

    补充一下

    其实社会发展到今天,凭现在的生产力,足以让所有人过上衣食无忧的生活。

    但是由于分配问题,由于资本的压榨本能,使得大部分人并没有因为生产力和科技的发展获得相应的幸福感。

    内卷的结果,是资本可以获得更加廉价的劳动力,资本增值更快。

    但是劳动力本身却更苦了。

    这也是为什么机器会吃人的根本原因。

    机器哪会吃人,是资本要吃人。

    尤其是在这个科技和知识是第一生产力的时代,内卷并不利于社会进步。

    因为知识生产需要一个比较宽松的社会环境。

    社会福利越好,知识生产反而会越佳。

    那种残酷竞争所带来的不安全感,实际上是不利于科技进步的。

    内卷状态下的知识生产者,是非常焦虑的。

    相应的,知识学习者也是极度焦虑,这样的学习效果其实并不好。

    这就是上面我专门举例高考和高校科研的原因所在。

    更何况,这种过分的竞争极大的降低了人们的安全感和幸福感。

    ……

    我所想象的理想世界是,每个人可以在解决温饱的前提下,做自己喜欢做的工作,焕发出最大的工作热情,从而促进社会进步。

    当然,这只是理想。

  • 数据流图 DFD

    • 据说 microSoft Visio 画这个很方便
  • 数据库设计,朱勇认为的,必须的三张表,三个字段

Java高级

  • 发现,使用 Try-with-Resource 配合 循环 进行使用时
    • 如果 Try-with-Resource循环体内
    • 那么,仅首次循环能正常访问资源。
    • 建议,将循环放在 Try-with-ResourceTry后花括号内
// 实例
public static void main(String[] args) {
System.out.println("实验二 第 1 题: 按字母的升序显示所有的单词,不能重复");
// HashSet<EnglishWord> wordSet = new HashSet<EnglishWord>();
try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) {
while (true) {
System.out.println("y|Y >>> 继续 \nelse >>> 退出");
char cs[] = br.readLine().toCharArray();
if (cs[0] != 'y' && cs[0] != 'Y') {
break;
} else {
System.out.println("\n1. 请在一行内输入所有的英文单词");
System.out.println("2. 新的一行将被重新单独排序");
System.out.println("3. 程序将忽略单词的大小写");
System.out.print(">>> ");
String inLine = br.readLine();
// 想用正则表达式,但是没能写好
if (inLine != null && inLine.matches("([a-zA-Z]\s)*")) {
System.out.println("成功读取: " + inLine + "\n");
} else {
System.out.println("输入格式不正确: " + inLine + "\n");
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
  • 计算中缀表达式,用 numStack配合opStack两个栈会更方便
  • 中缀转后缀并输出后缀表达式字符串,只用一个栈才更方便:运算符入栈,而数字直接出栈

周四

彭彬安卓

Activity 开始 —— 初步认识 Activity

  • 彭老师说:入口函数
  • 类比C语言、Java语言也是有 Main函数 的。
  • 程序在编译执行时,先执行程序语言框架的初始启动加载指令,
  • 在做好准备后,执行器去找固定的那个入口函数,从main的第一行指令再开始执行。

  • 彭老师说:消息循环
  • 电脑启动以后发生了什么?当电脑开机完成以后,大家看到一个桌面,这意味着什么?
  • 意味着,电脑的操作系统已经启动好了,
  • 经过了它自己的必要且复杂的启动过程,构建出了任务循环,进入等待。
  • 等待什么,等待用户做的任何事情
  • 比如双击,比如鼠标拖动
  • 这些事情,都会成为信息,进入操作系统准备好的 任务循环/消息循环
  • 收到消息以后,循环启动了,根据相应的消息,触发相应的操作,
  • 比如,我双击了Chrome浏览器图标,操作系统就会去找,找Chrome程序托管在操作系统里的
  • 操作系统打开这个,这个就是这次任务的入口函数
  • 启动Chrome浏览器这个任务本身会带来一些消息,返回给操作系统
  • 执行了启动Chrome这个任务,操作系统自己又会记录一些消息
  • 这些消息,有些会存在系统里,有些会以种种形式 交互 给用户
  • 消息/任务 在这个活动里 有进有出,这就是循环。

  • 彭老师说:生存环境
  • Linux生存在 C语言 编写的操作系统内核上
  • 安卓生存在 由Linux改编而来的 操作系统内核上
  • 基于安卓开发的APP 生存在 安卓提供的 内核服务上
    • 我说:
    • 内核服务是什么。就是 SDK,就是ADK,就是胡隽讲了很多次的请求内核服务
    • 也就是 计算机专业名词 Framework

  • 彭老师说:Activity
  • 之前提到的那个循环,就是一个Acitvity;Acitvity就是一个服务循环
  • Acitivity启动以后,就是在等待消息,然后再去做相应的触发事情
  • 重要的是:安卓应用的几乎每一个界面,都是一个 Activity
  • 所有的 Activity 都最终继承自 android.app.activity

安卓 HelloWorld 工程

打字稍慢,有点跟不上哈哈哈哈

  • 彭老师说:包名
  • 安卓是Java开发的,包名是从Java开发延续下来的习惯
  • 根据程序员们约定俗成的习惯,我们习惯将 域名 设置为 包名
  • 因为对咱们程序员来言,“域名,是一个人或者一个组织,在互联网上唯一的标示”

  • 彭老师说:安卓版本
  • 应用程序设定的:指定运行的安卓版本,是向上向后兼容的。
  • 如果这个应用程序是指定版本 Android 5.0,那么他应该是在 Android ≥ 5.0 的环境上能正常运行
  • 除非,某个安卓版本进行了的特别大的不可兼容的更新。
  • 比如 安卓10,安卓5 的 app 在安卓10上就可能会闪退,因为安卓10是一次很大的不向下向前兼容的更新。

  • 彭老师说:布局 算法 布局器 布局文件 设置配置
  • 布局,对程序员而言,就是 什么东西改摆放在哪里,该怎么摆的算法
  • 布局器,就是官方为我们提供的布局算法,省得我们自己再去开发了
  • 布局文件,就是对布局器提供的配置文件,能帮我们更好地使用布局器
    • 因为,布局算法,一般都挺复杂的,会有很多参数,很多选项之类的东西
    • 这些东西,为了方便,就从代码里抽离出来,放在专门的布局文件里
    • 布局器会加载这个文件,按这个文件的配置来实现具体的布局

  • 彭老师说:看看 HelloWorld 中的 Acitivity
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);

FloatingActionButton fab = findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
count += 10;
Snackbar.make(view, String.format("Replace with your own action: %d", count), Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
}
  • 彭老师问:什么是 @Override —— 是一个注解/标注
  • 彭老师问:看到 onCreate 大家想到了什么 —— 是一个 事件触发 的函数,在 create 时会触发的函数
  • 彭老师问:setContentView(R.layout.activity_main)
    • 观察参数:
    • R → Resource(资源)
    • layout → 布局
    • activity_main → 一个静态常量

  • 特殊的,R 不是程序员自己写的,是安卓这个框架自己生成的。
  • 安卓框架把所有的Resource资源编译到 R 这个public final class
  • 程序员在程序中通过访问 R 来访问工程的所有静态资源

彭老师说:结对编程(请百度,蛮好玩)两个人水平要差不多,效果才最好


彭老师说:所有在安卓程序里能被称为 组件 的东西,都是 View 这个类的子孙后代


彭老师说:XML文件,读作chā mile

“从今天起,咱们班任何一个同学都不能说自己不认识XML文件了啊”

XML格式:

  • 标签名,标签属性,标签嵌套
  • android:icon 里的 android 是一个命名空间icon 是这个空间下的一个属性名

C语言为什么从一般的应用开发里退出了。就是因为C语言没有“命名空间”,他的所有命名都是全局污染。

XML被发明出来是为了用于 数据交换 的。


彭彬 Java泛型 的知识补充

  • 泛型存在的必要性:为了实现,算法 和 算法的操作对象 二者的分离解耦。
  • 泛型不是语法糖
  • 泛型是在编译时,通过类型擦除来生效的。
  • .class 语法是Java提供的一个蓝图类,用于获取一个可以描述当前对象的类规范
  • .class蓝图类语法,是在运行时,通过反射机制来生效。
  • 泛型.class 语法相比,或许在,将”类型参数化“上可以起到相似或者相同的效果,但是在 算法 与 算法的操作对象 分离 的目标上,.class抽象层次没有泛型<> 这样彻底。“两者属于不同的抽象层次”。

实验课内容

  • 彭老师向大家热烈推荐了:<<阿里巴巴Java开发手册>>
  • 文件放在 FTP: penbdownload/2020智能终端与移动应用开发/课件/补充资料/阿里巴巴Java开发手册.pdf
  • 彭老师建议,这本书,每天看个 4~5 条,看不懂也留个印象在。
  • 并向大家简述了其中的两点
    • 【推荐】慎用 Object 的 clone 方法来拷贝对象。
      • 说明:对象的 clone 方法默认是浅拷贝,若想实现深拷贝需要重写 clone 方法实现属性对象 的拷贝
    • 【强制】关于 hashCode 和 equals 的处理,遵循如下规则:
      • 只要重写equals,就必须重写hashCode。
      • 因为Set存储的是不重复的对象,依据hashCode和equals进行判断,所以Set存储的 对象必须重写这两个方法。
      • 如果自定义对象做为Map的键,那么必须重写hashCode和equals。
      • 正例:String 重写了 hashCode 和 equals 方法,所以我们可以非常愉快地使用 String 对象 作为 key 来使用。

  • 关于 浅拷贝 和 深拷贝:我还是不清楚:如果对象只有“一层”,那么浅拷贝深拷贝效果相同?
  • 怎样实现一层的深拷贝呢?
  • 关于 hashcodeequals
    • 各自是什么
    • 为什么要一起重写
    • 什么时候要重写

释疑

  • 如果对象只有一层,那么浅拷贝和深拷贝效果相同

  • 对象的属性,

    • 如果属性的值是“普通值”,那么浅拷贝会直接复制并新建一个副本
    • 如果属性的值是“对象”,那么浅拷贝就仅仅是复制这个对象的引用
  • 对于 两个东西 相等 这件事,有两个概念

    • 一是,两个东西 的 值 相等,这由 equals() 来决定
    • 二是,两个东西 是 同一个东西,这由 hashCode() 来决定
  • 两个函数一起重写,能保证 两个东西的值相等时,就认定为同一个东西

  • 在 Java 提供的 集合类 中,这尤其有必要