参考资料
Eclipse
- 在preference里面, 搜索keys, 可以查看和修改快捷键
- Content Assist, 自动补全
- Format, 代码格式化
1. Java简介
1.1 历史
- Java 与 Internet 一起发展
www, 万维网, 所有信息用链接连接起来
Java, 静态网页 -> 动态网页
- Java的出现, 1995年, SUN, Stanford University Network, JDK 1.0
-
JDK, Java Development Kit, Java开发工具包
1995, JDK 1.0 1998, JDK 1.2, Java2 2000, JDK 1.3 2002, JDK 1.4, assert, logging, re 2004, JDK 1.5, 语法增加 2006, JDK 1.6, 广泛, Compiler API(动态编译), 脚本语言支持, WebService支持 2010, Oracle并购SUN 2011, JDK 1.7, 带资源的try, 重抛异常 2014, JDK 1.8, 大改进, lambda表达式 注: 从 JDK 1.5 之后, JDK 1.x 也被称为 JDK x, 如 JDK 1.8 也被叫做 Java 8
- Java的推动力
JCP, Java Community Process, 社区
JSR, Java Specification Requests, 规范
1.2 三大平台
- Java SE, J2SE, Java 2 Platform Standard Edition, 标准版, 桌面引用
- Jave EE, J2EE, Java 2 Platform Enterprise Edition, 企业版, Web应用
- Java ME, J2ME, Micro Edition, 微型版, 嵌入式设备
1.3 特点
- 跨平台, 安全稳定(不易内存溢出), 支持多线程, 丰富的类库
- 纯的面向对象, 变量和方法都在对象里面
-
与 C++ 的区别
- 无直接指针, 自动内存管理
- 基本数据类型长度固定
- 不使用头文件
- 不支持宏
- 无多重继承(使用接口)
- 没有(放在类外面的)全局变量
- 没有 GOTO
1.4.1 Java的编译与运行(IDE, 以 Eclipse 为例)
- 打开 Eclipse, 然后左上角 File --- New --- Java Project, 输入工程名 javanote --- Finish
- 在 Package Explorer 中展开 javanote --- 右键 src --- New --- Class, 在 Name 中输入 Main (首字母大写), 这之后我们会在 Package Explorer 中看到新增的 Main.java
-
编写 Main.java
package javanote; public class Main{ public static void main(String args[]) { System.out.println("hello world"); } }
- 运行
1.4.2 Java的编译与运行(命令行)
-
进入新建文件夹
./javanote
, 然后新建源程序文件Main.java
, 注意文件名和public class
后面的类名一致public class Main { // 注意 String[] args 不能省略 public static void main(String[] args){ System.out.println("hello world"); } }
-
编译, 将会得到
Main.class
目标文件(obj), 字节码bytecode
, 扩展名class
.它不是实际机器的最终执行代码# c 代表 compiler $ javac Main.java $ ls Main.class Main.java
-
运行
# 注意不是 java Main.class java Main
通过
JVM
读取并处理class
文件, 最终转化成CPU
的指令.JVM for Win/Unix/
模拟了一个操作系统/接口
1.5 三种核心机制
- Java Virtual Machine, Java虚拟机
源程序(
.java
后缀) ---javac--- 字节码(bytecode,.class
后缀) ---java--- 在 JVM 上运行JVM 规定了虚拟的CPU和内存, 包含以下内容: 寄存器集, 类文件结构, 堆栈, 垃圾收集堆, 内存区域
- Code Security, 代码安全性检测
- Garbage Collection, 垃圾回收, 自动管理内存
Java 程序不能依赖于垃圾回收的时间或者顺序
GC
是完全自动的, 不能被强制执行, 程序员最多只能用System.gc()
来建议执行垃圾回收器回收内存, 但是具体的回收时间, 是不可知的。当对象的引用变量被赋值为null
, 可能被当成垃圾GC
自动回收内存, 程序员不需要要无法精确控制回收过程, 也就是说只有new
, 没有delete
系统级线程会跟踪存储空间的分配情况
JVM 空闲时, 检查和释放那些可以释放的空间
1.6 Java 运行环境, JRE, Java Runtime Environment
- 在具体运行的时候, Java需要一个运行环境, 即
JRE
-
JRE = JVM + API(Lib)
-
JRE
运行程序时的三项主要功能加载代码(class loader), 校验代码(bytecode verifier), 执行代码(runtime interpreter, 因此虚拟机有时候也简单称为一个解释器)
小结: Java运行环境(JRE) 首先由虚拟机 JVM 来装载程序, 然后调用相应的指令来执行
- 平台无关: 把 class 文件放到不同的系统, 虚拟机可以执行, 不需要重新编译
1.7 Java 层次结构
-
JDK = JRE + Tools
(编译工具javac
, 打包工具jar
, 调试工具jdb
, 执行器java
, 文档生成器javadoc
)JRE = JVM + API
开发程序需要
JDK
, 如果只是运行程序则JRE
够了
1.8 面向对象简介
- 类, class, 是抽象的具有共同属性和行为的集合
类 = 属性(变量, 字段, field) + 行为(函数, 方法, method)
- 对象, object, 对象实例, instance, 一个实体, 一块可以标识的存储区域
- 三大特征: 封装, 继承, 多态
1.9 简单的 Java 程序
- 类型: Application(应用程序), Applet(小应用, 嵌入到HTML)
-
Application 的基本结构
package javanote; publicc class Main{ public static void main(String args[]){ System.out.println("Hello"); } } // 一个文件可以有多个 class, 但是只能有一个 public class, 且与文件同名 // main 方法必须是 public static // main 方法是一个特殊的方法, 它是程序运行的入口 // main 还可用于测试, 直接运行该类的时候会调用 main, 如果是其他类调用这个类的方法, 则不会运行 main // package, 包, 文件路径 // import, 导入其他的类
1.10 输入输出
-
输入 Scanner, 输出 System.out.print()
package javanote; import java.util.InputMismatchException; import java.util.Scanner; public class Main{ public static void main(String args[]) { try { // 注意, 如果输入的不是整数, 而是小数/字符串, 则会报错. 因此需要try...catch... Scanner in = new Scanner(System.in); int a = in.nextInt(); System.out.println(a); } catch (InputMismatchException e) { // TODO: handle exception } } }
1.11 基本数据类型
- 整数型
byte, 1 字节
short, 2 字节
int, 4 字节
long, 8 字节
Java 中没有
无符号数
- 实数, 浮点数
float, 4 字节
double, 8 字节, 浮点数默认是 double
-
逻辑型
boolean, 1 bit, true/false
Java 中不可以用
if(1)
或if(0)
, 也不能if( a = 5 )
boolean 不能比较大小, 不能进行算术运算
2 + true // error true + true // error, 就算换成 boolean a, b; 然后 a + b; 仍然是error true > false // error 1 > false // error true == 6; // error a == b > false // error, ==, != 优先级低, 先做 b > false, 这里也会出错 ( a == b ) > false // error, 类似于 true > false 的error
逻辑运算的
!, &&, ||
只能用于 boolean, 作用到其他类型上面会出错, 这点也和C
不一样 - 字符型
char, 2 字节, 统一使用 Unicode 编码, 跨平台
-
强制类型转换
double a = 10.3; int b = (int)a; int c = (int) (10/3.0); // (10/3.0)要加括号, 因为(int)是单目运算, 优先级高
1.12 对象存储区域
- 基本数据类型, 变量在栈. 复制变量的时候,复制的是值.
- 非基本数据类型, 引用类型, 在堆, 变量只是引用, 类似于指针, 只能指向特定对象, 不能乱指, 复制变量的时候,复制的是引用
1.13 数组
-
数组是一种容器, 所有元素有相同的数据类型, 一旦创建, 则不能改变大小. 数组必须用
new
来分配空间, 数组元素默认初始化.// 注意方括号的位置 // a和b都是数组, 其中 a 未分配空间, b 分配了 4 个 int 的空间 int[] a, b = new int[4]; // c 是数组, d 是 int 变量 int c[], d;
-
数组是引用类型, 元素个数可以用变量定义,
// 数组是引用类型 int[] a1 = new int[3]; int b = 10; // 元素个数可以用变量定义, 和c99类似 int[] a2 = new int[b]; // error, 数组是引用类型, 理解为指针, 不能直接给它分配空间, 分配的空间在堆, 必须 new 一块空间然后指向那里 int a3[5];
-
如果没有对数组进行显式初始化, 则会隐式初始化为 0 或 null, 比 C 安全
// 如果没有对数组进行显式初始化, 则会隐式初始化为 0 或 null int[] a4 = {3, 1, 2}; int[] a5 = new int[]{3, 1, 2}; // 注意, 数组元素会默认初始化, 但是基本数据类型声明后不会默认初始化 int a; a++; // error
-
与指针的类似之处, 数组变量只是数组的管理者, 而不是拥有者
int[] a = new int[10]; int[] b = a; b[0] = 5; // 此时 a[0] 也变成了 5 // a == b 的结果是 true
-
复制数组, 不能用
b = a
, 而需要遍历, 除此以外还有a.clone()
和System.arraycopy(src, int srcPos, dst, int dstPos, int length)
方法int[] a = {1, 2, 3, 4}; // 方法1, a.clone() int[] b = a.clone(); int[] c = new int[4]; // 方法2, System.arraycopy() System.arraycopy(a, 0, c, 0, a.length);
-
每个数组, 都有一个
.length
属性, 比C
安全int[] a = new int[10]; for( int i = 0; i < a.length; i++ ) { } // 也可以这样遍历, 因为数组是个 Iterable 对象 for( int n : a ) { }
-
二维数组
int[][] a = new int[3][5]; for( int i = 0; i < a.length; i++ ) { for( int j = 0; j < a[0].length; j++ ) { a[i][j] = i * 5 + j; } } // 初始化 int[][] a = { {1, 2, 3, 4}, {1, 2, 3}, } // 注意最后可以多一个逗号
1.14 字符, char, 字符串, String
- 字符类型 char, 能被单引号包围, 如 'a', '+', '你', unicode16编码, 2个字节, 在所有机器上是一致和统一的
-
字符串, String, 第一个字母大写, 说明是一个类, 是管理者
String s1 = new String("123"); String s2 = "abc"; // 自动转换 String s3 = s1 + s2 + 12 + 24; // "123abc1224" String s4 = s1 + s2 + (12 + 24); // "123abc36" // 注意 String s5 = null; System.out.println( s5 + "a" ); // nulla
-
读取输入
next --- 相当于 C++ 中的 cin nextLine --- 相当于 C++ 中的 getline
-
字符串的操作. 字符串是对象, 对它的所有操作都要通过
.
这个运算符- 长度,
s.length()
注意要加括号
()
, 是字符串的length()
方法, 和数组的length
属性不一样而且在调用
.length()
时,s
必须指向一块字符串, 不能是未初始化区域(null) - 访问字符,
s.charAt(index)
, 注意这是只读操作, read-only -
遍历, 注意字符串无法使用
for(char ch : s)
, 因为字符串不是Iterable
对象for( int i = 0; i < s.length(); i++ ){ s.charAt(i); }
-
子串,
.substring()
// [n:-1] s.substring(n); // [n, n + len) s.substring(n, len);
-
内容是否相同
.equals()
if( s.equals("Hello") ) { }
-
比较大小,
.compareTo()
, unicode 编码相减s1.compareTo(s2); // unicode 编码相减
-
其他操作
int loc = s.indexOf('a'); s.indexOf('a', loc + 1); s.indexOf("abc"); s.lastIndexOf('a'); s.startsWith(ch); s.endsWith(ch); s.trim(); // 去掉两端空格 s.replace(c1, c2); s.toLowerCase(); s.toUpperCase();
- 注意, 字符串本身是不可变的. 以上操作如果返回了字符串, 那这些返回的字符串是新生成的, 而不是修改了原来的字符串.
- 长度,
1.15 包裹类型, Wrapper
-
基本数据类型对应的包裹类型
boolean --- Boolean byte --- Byte short --- Short char --- Character int --- Integer long --- Long float --- Float double --- Double
-
基本数据类型 + 更多方法和字段
int a = Integer.MAX_VALUE; // 2^31 - 1 boolean b = Character.isDigit('a'); // false char c = Character.toLowerCase('A'); // a
-
数学类, Math
Math.abs() Math.round() Math.random() Math.pow()
1.16 方法(函数)
- 实参类型的宽度(尺寸大小)必须
<=
形参类型的宽度(大小)比如形参 double 实参 int 是可以的, 但是反过来就不行
- true 和 int 无法转换
- 方法的每一次运行, 都会产生独立的本地变量空间, 参数也是本地变量
由于没有 C 的指针, 也没有 C++ 的引用
&
, 所以一般方法无法实现swap
, 需要使用数组或者对象
1.17 命名习惯
- 类名首字母大写. 其余的, 首字母小写
- 少用下划线
- 随写随用,而不是上方统一声明、分开使用
1.18 修饰符
- 访问控制修饰符
- 其他修饰符
-
static
不属于某个实例, 而是属于整个类的(属性).
static
变量, 相当于该类一个全局变量, 有点像C
中的extern
全局变量, 是所有该类的对象实例所共享的. 这样看来,Java
的类是一个树的结构, 根结点中存储着公有信息, 而对象实例是衍生出来的子结点. 也就是说,static
是一个公共的路灯, 只有一盏, 每个人可以去开关路灯. 但是如果你要去关掉某一户人家里的灯, 就要明确指明他家的门牌号类就相当于计网中的协议, 比如网络层协议, 规定了每个数据包应该有什么样的格式. 而对象则是一个个具体的实际的数据包.
类变量, 相当于协议头部的一个字段, 所有这个类的对象实例都有相同的头部信息. 相对的, 成员变量则是数据部分.
static
变量单独划分一块存储空间, 不与具体的对象绑定在一起, 该存储空间被类的各个对象所共享.static
变量值在方法区加载一次, 而非 static
变量在创建对象时会加载很多次, 每次创建都会拷贝一份static
方法再内存中有专用的代码段 -
static
的访问static
方法可以访问static
变量 (类变量)static
方法不能访问无static
前缀的普通成员变量
, 不能访问实例变量, 也就是不能使用this
或super
, 调用类方法时, 可以通过 `类名.普通成员函数
可以访问static 变量
可以通过
类名.类变量
访问类变量, 也可以通过对象名.类变量
访问类变量. 同理, 调用类方法时可以用类名.类方法
, 也可以用类名.类方法
class A{ static int var = 1; public static void main(String args[]){ A.var++; // OK, 类名.类变量 A a = new A(); a.var++; // OK, 对象名.类变量 var++; // OK, 直接访问 } }
-
final
, 不可改变的, 最终的类前面加上
final
, 表示这个类不能被继承, 有利于 Java 的优化方法前面加上
final
, 则这个方法不能被子类覆盖Override
字段前面加上
final
, 能且只能被赋值一次, 然后就不能被改变了. 通常用static final
表示常量
2. 面向对象, OOP
2.1 类和对象
- 类 = 字段(成员变量) + 方法(成员函数)
成员变量的生存期 ---> 该对象的生存期, new 开始, GC 收集
成员变量的作用域 ---> 类的内部
-
构造方法, 构造函数, new 一个新的对象会调用构造方法, 可以重载 overload (参数表不同)
初始化顺序
1.new 2.先跳到构造函数(但不进入) 3.调到父类的构造函数(如果有父类) 4.重复3, 一直到最高的父类 5.跳到构造函数外面的 定义初始化 int price = 80, balance = 0; 6.进入构造函数, 完成构造函数里面的语句 7.回到子类, 进行子类的定义初始化, 再完成子类的构造函数 8.把构造出来的对象交给对象实例 vm 管理
如果有构造方法的重载, 而且如果其中一个要调用另一个构造方法, 那么要借助
this
class Person{ private int age, id; public Person(){ this(1);// 必须放在最开始, 调用另外一个构造方法 age = 0; } public Person(int pid){ id = pid; } }
析构函数使用的是
finalize()
方法, 但是一般不用去定义, 交给 JVM 就好了 - 对象是实体, 可以像理解 C++ 的指针一样理解对象实例
必须 new, 才会分配具体的空间, 每个对象的 id 不同
-
如果是用另一个对象对其赋值, 则相当于两个人管理同一个对象实例, 和指针一样
Student s1 = new Student(); Student s2 = s1; s1.setAge(5); // 5 s1.getAge(); // 5 s2.setAge(10); // s2 更新 age s1.getAge(); // 10, s1也会修改, 因为两者指向同一块区域
- 安全性
一个对象实例是内存中的一块区域, 只能通过同类型的引用去访问, 不能随便拿一个指针乱指到一个实例(内存区域)
2.2 访问控制
-
private
是对类的限制, 而不是对对象的限制
同一个类的内部, 该类的不同对象实例之间, 可以互相访问private.
但是出了这个类,就无法访问private了。举例如下
class Lower{ private int val = 0; public void test(){ Lower a = new Lower(); Lower b = new Lower(); // 类的内部, 该类的不同对象实例之间, 可以互相访问private a.val = b.val; } } class Higher{ public void test2(){ Lower l1 = new Lower(); Lower l2 = new Lower(); // error, not visible. 超出了Lower类的内部, private字段就变成了不可见 l1.val = l2.val; } }
private 方法和字段不能继承
-
public
-
default
(friendly)既没有前缀 public, 也没有前缀 private, 那么这个成员是
default
那么和它位于同一个包(同一个文件夹)的其他类可以访问.
注意, 不需要显式声明为
default
-
protected
同一包内的类可见
所有子类可见
- 继承关系
父类 public, 子类也必须为 public
父类 protected, 子类 protected, public, private
父类 private 不能够被继承
2.3 重载
- 方法重载: 多个方法有着相同的名字, 这些方法的签名 signature 不同 (参数不同, 或者参数类型不同), 编译的时候能够被识别出来
- 通过方法重载, 可以实现多态
2.4 封装
- 模块化, 把相关的属性和方法封装成为一个类
-
信息隐蔽, 隐藏类的细节(private), 用户只能通过受保护的接口访问某个类
class Person{ private int age; public int getAge(){ return age; } }
2.5 继承
- 父类(super)和子类(sub, 也作派生类)之间如果有共同的属性和方法, 就不用再写第二遍了。可以更好地抽象和分类, 提高代码的可重用性, 提高可维护性
- 单继承, 一个类只能有一个直接父类, 子类自动继承父类的状态和行为
-
继承的例子 (
extends
)class Person{ int age; String name; } // 注意关键字 extends 表示继承(扩展) class Student extends Person{ String school; } // 如果没有 extends xxx, 那么默认为 extends java.lang.Object 的子类 // 因此所有的类都是直接或间接地继承了 Object
- 覆盖
JDK 1.5 之后引入了
@Override
, 表示覆盖 (修改) 了父类的方法.可以当做注释, 同时编译器会帮你检查, 如果父类中没有相同的
方法名
+参数表
, 就会报错父类方法
func()
被覆盖后, 仍然可以用super.func()
调用父类的方法 - 重载
方法名相同, 参数表不同. 重载是新的方法.
-
构造方法是不能继承的, 因为构造方法是和类名同名的
子类构造的过程中需要用
super
来调用父类的构造方法例题, 问以下代码的结果是什么
class Person { String name = ""; public Person(String n) { name = n; } } class Employee extends Person { String empID = ""; public Employee(String id) { empID = id; } } public class Test { public static void main(String args[]) { Employee e = new Employee("123"); System.out.println(e.empID); } }
结果是编译报错, 因为父类
Person
没有无参数的构造函数, 解决方法// 方法1, 给父类 Person 增加无参数的构造函数(子类会默认调用) class Person { public Person() {} } // 方法2, 子类构造函数中用 super 显式调用父类已有的构造函数 class Employee extends Person { public Employee(String id) { super("Bob");// 注意 super 必须放在第一句 empID = id; } }
-
子类对象和父类对象的转换
Student
是一个Person
(子类对象可以被视为父类对象)Person
不一定是Student
, 具体情况看下面的例子// 假设 Student extends Person Person p1 = new Student(); // 编译正常, 运行正常 Student s1 = (Student) p1; // 编译正常, 运行正常 Person p2 = new Person(); Student s2 = (Student) p2; s2.xxx(); // 【编译正常, 运行错误】
2.6 包
- 与类的继承没有关系, 子类和父类可以位于不同的包中
- 相当于名字空间, 解决同名冲突. 同时包也相同于文件夹(存储路径)
- 另外就是可访问性, 同一个包中的各个类, 默认是可以互相访问的
2.7 抽象(abstract
) 和接口(interface
)
-
abstract
, 抽象abstract class Person{ // 含有抽象方法的类必须声明为抽象类 // 注意抽象方法是分号;结尾, 而不是花括号 // 也就是说抽象方法只有声明, 没有实现 abstract void speak(); void eat() { // 抽象类可以有非抽象方法 } } class Student extends Person{ @Override void speak() { // 子类 extends 父类后 // 要么仍然保持 abstract 方法 // 要么 Override 覆盖(这里也可以称为实现)父类的 abstract 方法 } } public class Main{ public static void main(String args[]) { // 错误, 抽象类不能被实例化 `instantiate` // Person p1 = new Person(); // 但是抽象类可以用于定义变量 Person p2 = new Student(); } }
-
interface
, 接口, 约定某种特征interface 是纯抽象类, 所有方法都是
abstract
, 也就是C++
中的纯虚类所有的成员变量都是
public static final
(相当于const
常量)static
表明它属于这个类而不是某个具体对象,final
表明它一旦初始化就不会被改变, 是一个编译时刻已经确定的常量// 注意直接用 interface 修饰, 不需要 class interface Flyable{ // 接口类的所有方法都是 `public abstract`, 不需要显式写出 // public abstract void fly(); void fly(); } interface Runnable{ // 如果使用 static 修饰符, 则表明这是默认实现, 不需要每次 implements 都去实现 run // java8 以上的新特性, 但是这就又回到父子继承了 static void run() { } } // implements 多个接口用逗号分隔 class Superman extends Person implements Flyable, Runnable{ @Override public void fly() { } } class Airplane implements Flyable{ @Override public void fly() { } }
接口可以继承接口,但是不能继承(extends)类
接口不能实现接口,只有类能够实现(implements)接口. 一个类可以实现(implements)多个接口(多继承), 也就是说, 通过接口可以实现不相关类的相同行为(
fly()
), 而不需要考虑层次关系(``) - 引用类型一共有三大类: 1.
类 class
, 2.接口 interface
, 3.数组
2.8 多态, polymorphism
- 相同的名字表示不同的含义
-
case1, 编译时多态, 静态
重载 Overload, 多个方法同名但参数表不同
p.say(); p.say("Hi");
-
case2, 运行时多态, 动态
覆盖 Override, 子类方法覆盖父类同名方法. 动态绑定 dynamic binding, 也称为虚方法调用, 运行的时候根据实例对象来调用方法, 举例如下
class Person{ void say(){ System.out.println("hi"); } } class Student extends Person{ @Override void say(){ System.out.println("hi, I'm a student"); } } public class Main{ static void func(Person p){ p.say(); } public static void main(String args[]) { Person p = new Person(); Student s = new Student(); Person ps = new Student(); // func() 的参数表说明了我们需要一个 Person 对象 func(p); // 但是我们也可以把 Student 传进去 // 这时候不会报错, 而且会正确调用子类的 say() func(s); // 更进一步, 如果我们用向上造型, func() 能接受 ps, 且会正确调用子类的 say() func(ps); } }
可以被
Override
的方法都可以称作虚方法, 虚方法不需要特殊的声明非
static、final、private
修饰的所有方法都是虚方法 - 向上造型 upcasting
父类 obj = new 子类();
子类的对象, 可以被当成父类的对象来使用, 可以赋值给父类的变量, 可以传递给需要父类对象的函数, 如果一个容器存放的是父类对象, 那么子类对象也可以放进去
// 为什么? 因为继承了父类, 对外的接口是一样的
子类的对象可以赋值给父类的变量. 注意,
Java
不存在对象
对对象
的赋值, 而是让两个管理员共同管理一个对象. 类比指针, 不是复制内容, 而是给地址.
2.9 OOP 设计思想
- 有哪些类, 类里面有什么属性和方法, 类之间的关系(继承, 关联), 对象之间发送消息(调用方法)
3. 容器
3.1 什么是容器
- 容器
container
, 是放东西的东西, 数组可以看做一种容器, 但是数组的长度一旦确定就无法改变, 而容器一般可以改变其容量 -
基本数据类型数组
与对象数组
// 基本数据类型数组, 每一个元素都是基本数据类型 int[] ai = new int[32]; // 每个元素被初始化为0 // String[] 数组, 对象数组, 每一个元素只是一个管理者, 一个指针, 而不是保存实际内容 // 元素初始化为 null, 实际内容还不存在, 需要 for 循环去创建和赋值(指向) String[] as = new String[10];
3.2 顺序容器 ArrayList
-
容器类
// import java.util.ArrayList; ArrayList<String> noteList = new ArrayList<String>(); // 用法参考 https://www.w3schools.com/java/java_arraylist.asp noteList.add( Integer.toString(0) ); noteList.add( "1" ); noteList.get(0); noteList.set(0, "00"); // import java.util.Collections; // 排序 Collections.sort(noteList); noteList.remove(0); noteList.clear(); // 如果要构建一个数组, 可以调用 toArray() 方法 int size = noteList.size(); String[] mylist = new String[size]; noteList.toArray(mylist);
- 关系图
3.3 Set
-
去重
HashSet<String> s = new HashSet<String>(); s.add("first"); s.add("second"); s.add("first"); sysout(s); // [first, second] s.contains("1"); // false
- 参考文档
3.4 Map
-
键值映射关系
/* 映射关系 1 penny 5 nickel 10 dime 25 quarter 50 half-dollar */ HashMap<Integer, String> dollarMap = new HashMap<Integer, String>(); dollarMap.put(1, "penny"); // 注意其他容器都是 add, map 是 put dollarMap.put(5, "nickel"); dollarMap.put(10, "dime"); dollarMap.put(25, "quarter"); dollarMap.put(50, "half"); dollarMap.replace(50, "half-dollar"); dollarMap.containsKey(30); // false dollarMap.get(30); // null dollarMap.get(50);
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。