Java知识总结
一、Java基础
1.java概述
Java语言的前身是Oak语言,美国Sun公司于1991(1995)年推出。2009年Sun公司被Oracle公司收购。
Java SE (java standard edition):标准版 – Java技术的核心和基础
Java EE (Java enterprise edition):企业版 – 企业经应用开发的一套解决方案
Java ME (Java micro edition):小型版 – 针对移动设备应用的解决方案
Java语言的特点:
- 语法简单,功能强大, 安全可靠。
- 可移植性高,与平台无关。
- 解释编译两种运行方式。
- 多线程。
- 动态执行兼有丰富的API文档及类库。
Java开发环境:
-
安装JDK:(Java SE Development Kit,Java语言软件开发工具包)包含编写和运行Java程序的所有工具,包括组成Java环境的基本构件。
-
配置环境变量
补充知识:
- JVM (Java Virtual machine):Java虚拟机,用于运行Java程序(Java跨平台的工作原理,编译的Java程序可以运行在不同平台的JVM虚拟机中)。
- JRE (Java runtime environment):Java运行时环境。
- JDK (Java development kit): Java开发工具包。
- 核心类库:Java提供的已经写好的程序和功能。
自动类型转换:类型范围小的变量可以直接赋值给类型范围大的变量。
byte a = 10;
int b = a;
2.数据和表达式
- 数据类型
- 基本数据类型:整型(byte、short、int、long)、浮点型(单精度浮点数、双精度浮点数)、字符型(char)和布尔型(也称逻辑类型,true、false)。
- 复合数据类型:数组、类和接口。
- 表达式
表达式由运算符和操作数组成,对操作数进行运算符指定的操作并得出运算结果。
运算符:
- 算术运算符:+ - * / %
- 关系运算符:< ,>, =, <=, >= ,==,!=
- 逻辑运算符:&& , || , !
- 位运算符:~ , & , | ,^ , << , >> , >>>
- 其他运算符: += , -= ,*= , /= , %= , |= , ^= , >>= , <<= , >>>= ,?:, 等等
a = a + b 与 a += b 的区别
+= 隐式的将加操作的结果类型强制转换为持有结果的类型。如果两个整型相加,如 byte、short 或者 int,首先会将它们提升到 int 类型,然后在执行加法操作。
double类型数值计算精确的问题
在运算时,是先转换成机器数在运算,也就是二进制。但在转换成二进制的时候,存储小数部分的位数会出现不够的情况,即无限循环小数,所以就造成了一下误差。
解决方案:使用BigDecimal类型转换一下即可。
3.流程控制语句
- 分支结构
- if
//格式1
if (条件表达式) {
语句体;
}
//格式2
if (条件表达式) {
语句体1;
} else {
语句体2;
}
//格式3
if (条件表达式1) {
语句体1;
} else if (条件表达式2) {
语句体2;
}
...
else {
语句体n+1;
}
- switch:匹配条件执行分支,适合做值匹配。
//格式
switch (表达式) {
case 值1:
执行代码...;
break;//不写break会出现穿透现象,不跳出,不再判断条件直接往下执行。
case 值2:
执行代码...;
break;
...
case 值n:
执行代码...;
break;
default:
执行代码...;
}
2.循环结构
- for循环
//格式
for (初始化语句 ; 循环条件 ; 迭代语句) {
循环体;
}
- while循环
//格式
while (循环条件) {
循环体;
迭代语句;
}
- do-while循环
初始化语句;
do {
循环体语句;
迭代语句;
} while (循环条件);
//特点:一定会先执行一次循环体。
3.循环控制语句
- break : 跳出并结束当前所在循环的执行。
- continue :跳出当前循环,进入下次循环。
4.面向对象程序设计
面向对象三大特征:封装、继承、多态
1.封装:Encapsulation
美 /ɪn,kæpsə’leʃən/
指隐藏对象的属性和实现细节,仅对外提供访问方式。
封装原则:
将不需要对外提供的内容都隐藏起来。
把属性都隐藏,提供公共方法对其访问。
好处:
- 减少耦合: 可以独立地开发、测试、优化、使用、理解和修改
- 减轻维护的负担: 可以更容易被程序员理解,并且在调试的时候可以不影响其他模块
- 有效地调节性能: 可以通过剖析确定哪些模块影响了系统的性能 提高软件的可重用性
- 降低了构建大型系统的风险: 即使整个系统不可用,但是这些独立的模块却有可能是可用的
匿名对象:
//当对对象的方法只调用一次时,可以使用匿名对象来完成。
//匿名对象示例
new 类名().方法名();
构造函数:
特点:
函数名与类名相同;不用定义返回值;不可以些return语句。
对象一建立就会调用与之对应的构造函数;
系统默认给没有自定义构造函数的类中结了一个空参数的构造函数。
作用:
给对象进行初始化。
构造代码块:
作用:给对象进行初始化。
与构造函数之间的区别:对象一建立就运行且由于构造函数执行;构造代码块是给所有的对象进行统一初始化,而构造函数是给对应的对象初始化。
//构造代码块简单示例
class Person
{
{ cry(); }
}
final关键字:
最终修饰符
示例:
//共有静态常量
public static final double PI = 3.14;
- 作为常量的书写规范:所有字母都大写,如果由多个单词组成,单词间通过下划线"_"连接。
特点:
- final可以用来修饰类、函数、变量
- 被final修饰的变量是一个常量,且只能赋值一次, 既可以修饰成员变量,也可以修饰局部变量
- 被final修饰的方法不可以被子类重写,但是可以被重载。
- 被final修饰的类不能有子类,不可以被继承。 为了避免继承时被子类复写其功能。例如Math、String
- 内部类定义在类中的局部位置上时,只能访问该局部被final修饰的局部变量
访问控制符/权限修饰符:
同一个类中 | 同一个包中 | 子类 | 所有类 | |
---|---|---|---|---|
private | OK | |||
default | OK | OK | ||
protected | OK | OK | OK | |
public | OK | OK | OK | OK |
- 通常类的属性设置为private(除非确定属性要让子类继承),对外提供获取属性和设置属性的public方法。
- set和get方法在eclipse中鼠标右键“source”–“Generate Getters and Setters”–快捷生成
- 一些只用于本类中的辅助性方法可以用private
- 一个Java源文件中最多只能有一个public类,当有一个public类时,源文件名必须与之一致,否则无法编译,如果源文件中没有一个public类,则文件名与类中没有一致性要求。
至于main()不是必须要放在public类中才能运行程序。
static关键字
- 静态的意思,用于修饰成员变量和成员方法。
- static修饰成员变量表示该成员变量在内存中只存储一份,可以被共享访问、修改。
- 静态成员变量和方法可以通过 类名.变量名/类名.方法名 这种方式直接访问。
2.继承
在Java中,用关键字extends表示派生/继承。
Object类是Java程序中所有类的直接或间接父类,处在类层次的最高层。
继承的特点:
- 子类可以继承父类的属性和行为,但是子类不能继承父类的构造器。
- java是单继承模式,即一个类只能继承一个直接父类。
- Java不支持多继承,但是支持多层继承。
- Java中所有的类都是Object类的子类。
继承后构造器的特点:
- 子类中所有的构造器默认都会先访问父类中的无参构造器,在执行自己的构造器。
因为子类在初始化的时候,有可能会使用到父类的数据,如果父类没有完成初始化,子类将无法使用父类的数据。
this和super关键字:
- this:代表本类对象的引用。
- super:代表父类存储空间的表示。
this(…)访问本类构造器 / super(…) 访问父类构造器
重写(覆盖)和重载:
重载:子类父类方法名相同,但是参数刘表不同。调用重载方法时,编译器将根据参数的个数和类型,选择对应的方法执行。
重写(覆盖):字父类方法名以及参数都一样,
- 重载的方法属于同一个类,重写的方法分属于父类、子类中。
3.多态
概述:
同一个对象,在不同时刻表现出来的不同形态
多态分为编译时多态和运行时多态:
- 编译时多态主要指方法的重载
- 运行时多态指程序中定义的对象引用所指向的具体类型在运行期间才确定
多态存在的三个必要条件:
- 1要有继承或实现关系;2要有方法重写;3(向上转型)父类引用指向子类对象
成员访问特点
-
成员变量:编译看父类,运行看父类
-
成员方法:编译看父类,运行看子类
-
代码演示
//动物类 public class Animal { public int age = 40; public void eat() { System.out.println("动物吃东西"); } }
//猫类 public class Cat extends Animal { public int age = 20; public int weight = 10; @Override public void eat() { System.out.println("猫吃鱼"); } public void playGame() { System.out.println("猫捉迷藏"); } }
//测试类
public class AnimalDemo {
public static void main(String[] args) {
//有父类引用指向子类对象
Animal a = new Cat();
System.out.println(a.age);
a.eat();
}
}
//40
//猫吃鱼
多态的好处和弊端
-
好处:提高程序的扩展性。定义方法时候,使用父类型作为参数,在使用的时候,使用具体的子类型参与操作
-
弊端:不能使用子类的特有成员
多态中的转型
-
向上转型:父类引用指向子类对象就是向上转型
-
向下转型:格式:子类型 对象名 = (子类型)父类引用;
-
代码演示
//动物类 public class Animal { public void eat() { System.out.println("动物吃东西"); } }
//猫类 public class Cat extends Animal { @Override public void eat() { System.out.println("猫吃鱼"); } public void playGame() { System.out.println("猫捉迷藏"); } }
//测试类 public class AnimalDemo { public static void main(String[] args) { //多态 //向上转型 Animal a = new Cat(); a.eat(); //向下转型 Cat c = (Cat)a; c.eat(); c.playGame(); } }
abstract 抽象
美 /'æbstrækt/
抽象方法中只有方法名,没有方法体。
示例代码:
abstract class Student{
abstract void study();
}
class MidStudent extends Student{
void study(){
System.out.println("MidStudent study");
}
}
为什么要定义抽象类?
是一种模板模式,抽象类为所有子类提供一个通用模板,子类可以在这个模板基础上进行扩展。通过抽象类可以避免子类设计的随意性,使子类之间更加通用。
- 抽象将方法的设计和方法的实现分离开来。
抽象的特点:
- 抽象方法一定是定义在抽象类中,抽象方法不能有主体,以分号“;”结束
- 抽象方法和抽象类都必须被关键字abstract修饰
- 抽象类可以包含属性、方法、构造方法,但是不能被new实例化
- 抽象类只能用来继承
- 抽象类中的方法要被使用,就必须由其子类复写其所有的抽象方法后,建立子类对象进行调用。
注意事项:
- 如果该子类只覆盖了部分抽象方法,那么该子类还是一个抽象类;即:要么覆盖全部抽象方法,要么子类也抽象。
- 子类复写其父类方法的时候,两者的返回值类型必须一样。
接口 interface
格式示例:
public interface MyInterface {
String NAME = "xiaoming";
public void test01();
public int test02(int a , int b);
}
接口中只有常量 和 抽象方法,修饰符是固定的:
- 成员变量:public static final
- 成员函数:public abstract
修饰符写或者不写都一样
特点:
- 接口不可以创建对象,因为包含抽象方法。
- 子类对接口中的方法全部重写后,子类才能实例化,否则子类是一个抽象类。
子类名 implements 接口名
-
一个类只能继承一个父类,但可以同时实现多个接口。
class 子类名 extends 父类名 implements 接口1,接口2,接口3...
-
接口与接口之间也可以继承,且可以多继承(一对多的情况下,接口中同名抽象方法的返回值类型应当相同)。
5.数组和字符串
1.数组
数组是相同数据类型的元素按一定顺序排列的集合。
数组的定义格式:
- 1.静态初始化数组(定义数组的时候直接给数组赋值)。
数据类型[] 数组名 = new 数据类型[]{元素1,元素2...};
//简化写法
数据类型[] 数组名 ={元素1,元素2...};
- 2.动态初始化数组(只情定元素的类型和数组的长度,之后再存入具体数据)。
数据类型[] 数组名 = new 数据类型[长度];
数组的遍历
//for循环遍历
for(int i = 0; i < 数组名.length; i++) {
数组名[i];
}
//foreach增强for循环JDK5开始
for (数组数据类型 i : 数组名) {
i;
}
2.字符串
String
String对象是不可变的,String类中修改String值的方法实际上都是创建了一个全新的String对象,以包含修改后的字符串内容。而最初的String对象则丝毫未动。
public class Immutable {
public static String upcase(String s) {
return s.toUpperCase();
}
public static void main(String[] args) {
String q = "xiangbian";
System.out.println(q);
String qq = upcase(q);
System.out.println(qq);
System.out.println(q);
}
}
/*
xiangbian
XIANGBIAN
xiangbian
*/
重载“+”与StringBuilder
用于String的“+”与“+=‘是Java中仅有的两个重载过的操作符,而Java并不允许程序员重载任何操作符。
public class WhitherStringBuilder {
public static String implicit(String[] fields) {
String result = "";
for (int i = 0; i < fields.length; i++) {
result+=fields[i];
}
return result;
}
public static String explicit(String[] fields) {
StringBuilder result = new StringBuilder();
for (int i = 0; i < fields.length; i++) {
result.append(fields[i]);
}
return result.toString();
}
public static void main(String[] args) {
String[] s = {"1","adfdf","fdf","123131"};
System.out.println(implicit(s));
System.out.println(explicit(s));
}
}
/*
1adfdffdf123131
1adfdffdf123131
*/
以上两种方法结果一样,实则内部实现不一样。
implicit(String[] fields) 会创建多个StringBuilder对象,而explicit(String[] fields)方法只会生成一个StringBuilder对象。
String上的操作
方法 | 参数 | 应用 |
---|---|---|
构造器 | 重载版本:默认版本,String,StringBuilder,StringBuffer,char数组,byte数组 | 创建String对象 |
length() | String中字符的个数 | |
charAt() | Int索引 | 取得String中该索引位置上的char |
getChars(),getBytes() | 要复制部分的起点和终点的索引,复制的目标数组,目标数组的起始索引 | 复制char或byte到目标数组中 |
toCharArray() | 生成一个char[],包含String的所有字符 | |
equals(),equalsIgnoreCase() | 与之进行比较的String | 比较两个String的内容是否相同 |
compareTo() | 与之进行比较的String | 按词典顺序比较String的内容,比较结果为负数、零、或正数。注意,大小写并不等价。 |
contains() | 要搜索的CharSequence | 如果该String对象包含参数的内容,则返回true |
contentEquals() | 与之进行比较的CharSequence或StringBuffer | 如果该String对象与参数的内容完全一致,则返回true |
equalsIgnoreCase() | 与之进行比较的String | 忽略大小写,如果两个String的内容相同,则返回true |
regionMatcher() | 该String的索引偏移量,另一个String及其索引偏移量,要比较的长度。重载版本增加了”忽略大小写“功能 | 返回boolean结果,以表明所比较区域是否相等 |
startsWith() | 可能的起始String。重载版本在参数中增加了偏移量 | 返回boolean结果,以表明该String是否以此参数起始 |
endsWith() | 该String可能的后缀String | 返回boolean结果,以表明此参数是否该字符串的后缀 |
indexOf(),lastIndexOf() | 重载版本包括:char,char与起始索引,String,String与起始索引 | 如果该String并不包含此参数,就返回-1,否则返回此参数在String中的起始索引。lastIndexOf()是从后向前搜索 |
substring() (subSequence()) | 重载版本:起始索引;起始索引+终点坐标 | 返回一个新的String,以包含参数指定的字符串 |
concat() | 要连接的String | 返回一个新的String对象,内容为原始String连上参数String |
replace() | 要替换的字符,用来进行替换的新字符。也可以用一个CharSequence来替换另一个CharSequence | 返回替换字符后的新String对象。如果没有替换发生,则返回原始的String对象 |
toLowerCase(),toUpperCase() | 将字符的大小写改变后,返回一个新String对象。如果没有发生改变,则返回原始的String对象 | |
trim() | 将String两端的空白字符删除后,返回一个新的String对象。如果没有改变发生,则返回原始的String对象 | |
valueOf() | 重载版本:Object;char[];char[],偏移量,与字符个数;boolean;char;int;long;float;double | 返回一个表示参数内容的String |
intern() | 为每个唯一的字符序列生成一个且仅生成一个String引用 | |
从表中可以看出,当需要改变字符串的内容时,String类的方法都会返回一个新的String对象。、
同时,如果内容没有发生改变,String的方法只是返回指向原对象的引用,以节约存储空间和避免额外的开销。
格式化输出System.out.format()
public class SimpleFormat {
public static void main(String[] args) {
int x = 5;
double y = 3.1415926;
System.out.println("ROW 1:[" + x +" "+y+"]");
System.out.format("Row 1:[%d %f]\n", x,y);
//or
System.out.printf("Row 1:[%d %f]\n", x,y);
}
}
/*
ROW 1:[5 3.1415926]
Row 1:[5 3.141593] 精度丢失
Row 1:[5 3.141593]
*/
format()与printf()是等价的,它们只需要一个简单的格式化字符串,加上一串参数即可,每个参数对应一个格式修饰符。
格式化说明符
在插入数据时,如果想要控制空格与对齐,你需要更精细复杂的格式修饰符。
抽象语法:
%[argument_index$][flags][width][.precision]conversion
用格式修饰符来打印一个购物收据:
import java.util.*;
public class Receipt {
private double total = 0;
private Formatter f = new Formatter(System.out);
public void printTitle() {
f.format("%-15s %5s %10s\n", "Item","Qty","Price");
f.format("%-15s %5s %10s\n", "-------","---","-----");
}
public void print(String name,int qty,double price) {
f.format("%-15.15s %5d %10.2f\n", name,qty,price);
total+=price;
}
public void printTotal() {
f.format("%-15s %5s %10.2f\n", "Tax"," ",total*0.06);
f.format("%-15s %5s %10s\n", " "," ","-----");
f.format("%-15s %5s %10.2f\n", "Total","",total*1.06);
}
public static void main(String[] args) {
Receipt receipt = new Receipt();
receipt.printTitle();
receipt.print("Jack's Magic Beans", 4, 4.25);
receipt.print("Princess Peas", 3, 5.1);
receipt.print("Three Bears Porridge", 1, 14.29);
receipt.printTotal();
}
}
/*
Item Qty Price
------- --- -----
Jack's Magic Be 4 4.25
Princess Peas 3 5.10
Three Bears Por 1 14.29
Tax 1.42
-----
Total 25.06
*/
Formatter
常用类型转换字符:(占位符)
%d | 整数型 |
%c | Unicode字符 |
%b | Boolean值 |
%s | String |
%f | 浮点数(十进制) |
%e | 浮点数(科学计数) |
%x | 整数(十六进制) |
%h | 散列码(十六进制) |
%% | 字符% |
代码示例:
import java.util.*;
public class Conversion {
public static void main(String[] args) {
Formatter f = new Formatter(System.out);
char c = 'a';
System.out.println("c = 'a'");
f.format("%%c: %c\n", c);
f.format("%%s: %s\n", c);
f.format("%%b: %b\n", c);
f.format("%%h: %h\n", c);
//f.format("%%d: %d\n", c);
f.close();
}
}
/*
c = 'a'
%c: a
%s: a
%b: true
%h: 61
*/
注意:对于b转换而言,boolean基本类型或Boolean对象以外的其他类型的参数,只要该参数不为null,那转换结果就永远都是true。
正则表达式
正则表达式是一种强大而灵活的文本处理工具,它提供了一种完全通用的方式,能够解决各种字符串处理相关的问题:匹配、选择、编辑以及验证。
字符:
B | 指定字符B |
\xhh | 十六进制值为oxhh的字符 |
\uhhh | 十六进制表示为oxhhhh的Unicode字符 |
\t | 制表符tab |
\n | 换行符 |
\r | 回车 |
\f | 换页 |
\e | 转义(Escape) |
字符类:
. | 任意字符 |
[abc] | 包含了a、b和c的任何字符(和a|b|c作用相同) |
[^abc] | 除了a、b和c之外的任何字符(否定) |
[a-zA-z] | 从a到z或从A到Z的任何字符(范围) |
[abc[hij]] | 任意a、b、c、h、i、和j字符(与a|b|c|h|i|j作用相同)(合并) |
a-z&&[hij] | 任意h、i或j(交) |
\s | 空白符(空格、tab、换行、换页和回车) |
\S | 非空白符([^\s]) |
\d | 数字[0-9] |
\D | 非数字[0-9] |
\w | 词字符[a-zA-Z0-9] |
\W | 非词字符 |
逻辑操作符:
XY | Y跟在X后面 |
x|y | X或Y |
(X) | 捕获组(capturing group)。可以在表达式中用\i引用第i个捕获组 |
边界匹配符:
^ | 一行的起始 |
$ | 一行的结束 |
\b | 词的边界 |
\B | 非词的边界 |
\G | 前一个匹配的结束 |
量词:
- 贪婪型: 量词总是贪婪的,除非有其他的选项被设置。贪婪表达式会为所有可能的模式发现尽可能多的匹配。导致此问题的一个典型理由就是假定我们的模式仅能匹配第一个可能的字符组,如果他是贪婪的,那么它就会继续往下匹配。
- 勉强型: 用问号来指定,这个量词匹配满足模式所需的最少字符数。也称懒惰的、匹配的、非贪婪的、不贪婪的。
- 占有型: 目前这种量词只有在Java语言中可用。当正则表达式被应用与字符串时,他会产生相当多的状态,以便在匹配失败时可以回溯。而“占有型”量词并不保存这些中间状态,因此它们可以防止回溯。它们常常用于防止正则表达式失控,因此可以使正则表达式执行起来更有效。
贪婪型 | 勉强型 | 占有型 | 如何匹配 |
---|---|---|---|
X? | X?? | X?+ | 一个或零个X |
X* | X*? | X*+ | 零个或多个X |
X+ | X+? | X++ | 一个或多个X |
X{n} | X{n}? | X{n}+ | 恰好n次X |
X{n,} | X{n,}? | X{n,}+ | 至少n次X |
X{n,m} | X{n,m}? | X{n,m}+ | X至少n次,且不超过m次 |
表达式X通常必须要用圆括号括起来,以便它可以按照我们期望的效果去执行。
例如:
abc+ //这个表达式表示:匹配ab,后面跟随1个或多个c。
要表明匹配一个或多个完整的abc字符串,必须这要表示:
(abc)+ //匹配一个或多个完整的abc字符串
6.泛型
- Java泛型这个特性是从JDK 1.5才开始加入的,因此为了兼容之前的版本,Java泛型的实现采取了“伪泛型”的策略,即Java在语法上支持泛型,但是在编译阶段会进行所谓的“类型擦除”(Type Erasure),将所有的泛型表示(尖括号中的内容)都替换为具体的类型(其对应的原生态类型),就像完全没有泛型一样。
- 泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。
- 引入泛型的意义在于: 适用于多种数据类型执行相同的代码(代码复用)
定义泛型方法语法格式:
调用泛型方法语法格式
7.集合(容器)
1.容器介绍
容器,就是可以容纳其他Java对象的对象。
*Java Collections Framework(JCF)*为Java开发者提供了通用的容器,其始于JDK 1.2,
优点是:
- 降低编程难度 提高程序性能
- 提高API间的互操作性
- 降低学习难度 降低设计和实现相关API的难度
- 增加程序的重用性
Java容器里只能放对象,对于基本类型(int, long, float, double等),需要将其包装成对象类型后(Integer, Long, Float, Double等)才能放到容器里。很多时候拆包装和解包装能够自动完成。这虽然会导致额外的性能和空间开销,但简化了设计和编程。
2.集合体系结构
- 容器主要包括 Collection 和 Map 两种,Collection 存储着对象的集合,而 Map 存储着键值对(两个对象)的映射表。
3.Collection
Set
- TreeSet
基于红黑树实现,支持有序性操作,例如根据一个范围查找元素的操作。但是查找效率不如 HashSet,HashSet 查找的时间复杂度为 O(1),TreeSet 则为 O(logN)。 - HashSet
基于哈希表实现,支持快速查找,但不支持有序性操作。并且失去了元素的插入顺序信息,也就是说使用 Iterator 遍历 HashSet 得到的结果是不确定的。 - LinkedHashSet
具有 HashSet 的查找效率,且内部使用双向链表维护元素的插入顺序。
List
- ArrayList
基于动态数组实现,支持随机访问。 - Vector
和 ArrayList 类似,但它是线程安全的。 - LinkedList
基于双向链表实现,只能顺序访问,但是可以快速地在链表中间插入和删除元素。不仅如此,LinkedList 还可以用作栈、队列和双向队列。
Queue
-
LinkedList
可以用它来实现双向队列。 -
PriorityQueue
基于堆结构实现,可以用它来实现优先队列。
4.Map
- TreeMap 基于红黑树实现。
- HashMap 基于哈希表实现。HashTable 和 HashMap 类似,但它是线程不安全的,这意味着同一时刻多个线程可以同时写入 HashTable 并且不会导致数据不一致。它是遗留类,不应该去使用它。现在可以使用 ConcurrentHashMap 来支持线程安全,并且 ConcurrentHashMap 的效率会更高,因为 ConcurrentHashMap 引入了分段锁。
- LinkedHashMap 使用双向链表来维护元素的顺序,顺序为插入顺序或者最近最少使用(LRU)顺序。
8.多线程
9.反射
二、数据库
1.基础
模式定义了数据如何存储、存储什么样的数据以及数据如何分解等信息,数据库和表都有模式。 主键的值不允许修改,也不允许复用(不能使用已经删除的主键值赋给新数据行的主键)。
SQL(Structured Query Language),标准 SQL 由 ANSI 标准委员会管理,从而称为 ANSI SQL。各个 DBMS 都有自己的实现,如 PL/SQL、Transact-SQL 等。
SQL 语句不区分大小写,但是数据库表名、列名和值是否区分依赖于具体的 DBMS 以及配置。
2.创建表
CREATE TABLE mytable (
id INT NOT NULL AUTO_INCREMENT,
col1 INT NOT NULL DEFAULT 1,
col2 VARCHAR(45) NULL,
col3 DATE NULL,
PRIMARY KEY (`id`));
3.修改表
- 添加列
ALTER TABLE mytable
ADD col CHAR(20);
- 修改列和属性
---ALTER TABLE 表名 CHANGE 原字段名 新字段名 字段类型 约束条件
ALTER TABLE mytable
CHANGE col col1 CHAR(32) NOT NULL DEFAULT '123';
- 删除列
ALTER TABLE mytable
DROP COLUMN col;
- 删除表
DROP TABLE mytable;
4.插入
- 普通插入
INSERT INTO mytable(col1, col2)
VALUES(val1, val2);
- 插入检索出来的数据
INSERT INTO mytable1(col1, col2)
SELECT col1, col2
FROM mytable2;
- 将一个表的内容插入到一个新表
CREATE TABLE newtable AS
SELECT * FROM mytable;
5.更新
UPDATE mytable
SET col = val
WHERE id = 1;
6.删除
使用更新和删除操作时一定要用 WHERE 子句,不然会把整张表的数据都破坏。可以先用 SELECT 语句进行测试,防止错误删除。
DELETE FROM mytable
WHERE id = 1;
- TRUNCATE TABLE 可以清空表,也就是删除所有行。
TRUNCATE TABLE mytable;
7.查询
- DISTINCT:相同值只会出现一次。它作用于所有列,也就是说所有列的值都相同才算相同。
SELECT DISTINCT col1, col2
FROM mytable;
- LIMIT:限制返回的行数。可以有两个参数,第一个参数为起始行,从 0 开始;第二个参数为返回的总行数。
返回前5行:
SELECT *
FROM mytable
LIMIT 5;
SELECT *
FROM mytable
LIMIT 0, 5;
返回3~5行:
SELECT *
FROM mytable
LIMIT 2, 3;
8.排序
- ASC : 升序(默认)
- DESC : 降序
可以按多个列进行排序,并且为每个列指定不同的排序方式:
SELECT *
FROM mytable
ORDER BY col1 DESC, col2 ASC;
9.过滤
不进行过滤的数据非常大,导致通过网络传输了多余的数据,从而浪费了网络带宽。因此尽量使用 SQL 语句来过滤不必要的数据,而不是传输所有的数据到客户端中然后由客户端进行过滤。
SELECT *
FROM mytable
WHERE col IS NULL;
WHERE 子句可用的操作符:
操作符 | 说明 |
---|---|
= | 等于 |
< | 小于 |
> | 大于 |
<> != | 不等于 |
<= !> | 小于等于 |
>= !< | 大于等于 |
BETWEEN | 在两个值之间 |
IS NULL | 为 NULL 值 |
应该注意到,NULL 与 0、空字符串都不同。
AND 和 OR 用于连接多个过滤条件。优先处理 AND,当一个过滤表达式涉及到多个 AND 和 OR 时,可以使用 () 来决定优先级,使得优先级关系更清晰。
IN 操作符用于匹配一组值,其后也可以接一个 SELECT 子句,从而匹配子查询得到的一组值。
NOT 操作符用于否定一个条件。
10.通配符
通配符也是用在过滤语句中,但它只能用于文本字段。
- % 匹配 >=0 个任意字符;
- _ 匹配 ==1 个任意字符;
- [ ] 可以匹配集合内的字符,例如 [ab] 将匹配字符 a 或者 b。用脱字符 ^ 可以对其进行否定,也就是不匹配集合内的字符。
使用 Like 来进行通配符匹配:
SELECT *
FROM mytable
WHERE col LIKE '[^AB]%'; -- 不以 A 和 B 开头的任意文本
不要滥用通配符,通配符位于开头处匹配会非常慢。
11.计算字段
计算字段通常需要使用 AS 来取别名,否则输出的时候字段名为计算表达式。
SELECT col1 * col2 AS alias
FROM mytable;
CONCAT() 用于连接两个字段。许多数据库会使用空格把一个值填充为列宽,因此连接的结果会出现一些不必要的空格,使用 TRIM() 可以去除首尾空格。
SELECT CONCAT(TRIM(col1), '(', TRIM(col2), ')') AS concat_col
FROM mytable;
12.函数
各个 DBMS 的函数都是不相同的,因此不可移植,以下主要是 MySQL 的函数。
汇总
函 数 | 说 明 |
---|---|
AVG() | 返回某列的平均值 |
COUNT() | 返回某列的行数 |
MAX() | 返回某列的最大值 |
MIN() | 返回某列的最小值 |
SUM() | 返回某列值之和 |
AVG() 会忽略 NULL 行。
使用 DISTINCT 可以让汇总函数值汇总不同的值。
SELECT AVG(DISTINCT col1) AS avg_col
FROM mytable;
文本处理
函数 | 说明 |
---|---|
LEFT() | 左边的字符 |
RIGHT() | 右边的字符 |
LOWER() | 转换为小写字符 |
UPPER() | 转换为大写字符 |
LTRIM() | 去除左边的空格 |
RTRIM() | 去除右边的空格 |
LENGTH() | 长度 |
SOUNDEX() | 转换为语音值 |
其中, SOUNDEX() 可以将一个字符串转换为描述其语音表示的字母数字模式。
SELECT *
FROM mytable
WHERE SOUNDEX(col1) = SOUNDEX('apple')