Java基础

配环境

安装Jre和IDEA

去IDEA官网下载:

https://www.jetbrains.com/idea/download/download-thanks.html?platform=windows

之前装了jdk,不用再下了

关于jdk和jre的区别:

JRE: Java Runtime Environment,顾名思义是java运行时环境,包含了java虚拟机,java基础类库。是使用java语言编写的程序运行所需要的软件环境,是提供给想运行java程序的用户使用的。

JDK:Java Development Kit JREJDK,顾名思义是java开发工具包,是程序员使用java语言编写java程序所需的开发工具包,是提供给程序员使用的。JDK包含了JRE,同时还包含了编译java源码的编译器javac,还包含了很多java程序调试和分析的工具:jconsole,jvisualvm等工具软件,还包含了java程序编写所需的文档和demo例子程序。

如果你需要运行java程序,只需安装JRE就可以了。如果你需要编写java程序,需要安装JDK。

如何使用IDEA

打开idea,创建工程,在左边的src文件中选择创建java class

创建之后,随便写个main函数,按alt+shift+f10运行

基本语法

主类结构

Java是面向对象的语言,因此Java程序的基本构成是类,与C和C++不同的是,main方法也必须包括在类中,这个含有main方法的类叫做主类

main方法的声明:

1
2
3
4
5
public class 主类名×××{
public static void main(String[] args){
main方法体×××;
}
}

导入

可以使用import导入相应的类,类似于C++中的include语句

基本数据类型

Java的所有类型的长度都是固定的,因此具有平台无关性,C++则不是,C++的指针类型,int类型,long类型等都有可能会因为编译器位数的不同产生改变

如int型,在16位编译器上长度为2字节,在32位和64位编译器上则长度为32字节

整形

变量

和C++位编译下整数类型基本一致,只不过没有char型整形,取而代之的是byte型,范围和char型一致,为1字节;没有long long型,有long型,为8字节

常量

java的整数常数有十进制,八进制,十六进制3种表示形式

十进制

正常十进制表示形式即可,不能加前导零

八进制

以数字0开头的都是八进制数,如089,0111

十六进制

以0x或0X开头的为十六进制数

Java默认整数常量为int型,如果常数超过int的最大表示范围,需要在末尾加一个l或L

浮点类型

变量

浮点类型有float和double两种,和64位下的C++的float与double型长度一致

常量

Java默认浮点常量为double型,如果想让常量为float型,需要在末尾加一个f或F

常量末尾加的类型首字母标明了常量的类型,若没有则会使用默认类型

字符类型

char型

变量

Java的char型占2个字节,表示一个字符,赋值变量时,可以使用单个字符赋值,也可以使用[0,65535]中的整数直接赋值

和C++一样,Java中的字符类型也可以直接当成整数处理

常量

Java中的字符常量使用单引号表示,字符串使用双引号,与C++一致

转义字符

\+数字(如\123),八进制数表示的字符

\+u+数字(如\u0052),十六进制数表示的字符

\+’,单引号

\\,反斜杠

\t,制表符

\n,换行

\b,回车

布尔类型

布尔类型的关键字变为bollean,与C++不同,bollean类型不能和整数类型进行转换

变量与常量

标识符

Java的标识符规则基本与C++一致,不同的一点是Java变量名中也可以使用$符号

声明变量

声明变量的规则和C++一致

声明常量

Java可以声明专门的常量,常量只能被赋值一次,在普通的变量名前面加一个final关键字即可声明一个常量

一个习惯:Java的常量名一般全部使用大写字母

变量的作用范围

类中的成员变量在整个类中都有效,而方法中的变量仅仅在方法里有效

运算符

Java的运算符和C++基本一致

强制类型转换

Java的强制类型转换与C++基本一致

注释

注释和C++基本一致

流程控制语句

由于和C++高度一致,只说不一样的部分:

foreach语句

对于数组和集合,可以使用,语法如下:

1
2
3
4
//x是一个数组或集合
for(int i: x){
×××
}

字符串

Java中的字符串属于java.lang包中的String类,字符串使用双引号括起来

创建字符串

1 使用字符数组创建字符串

1
2
char a\[\] = {'g', 'o', 'o', 'd'};
String str = new String(a);

str = “good”

2 使用字符数组的一部分创建字符串

1
2
char a\[\] = {'a', 'b', 'c', 'd', 'e', 'f'};
String str = new String(a, 2, 4);//2是起始下标,4是长度

str = “cdef”

3 通过常量

1
2
String str1 = "shit";
String str2 = "shit";

此时str1和str2引用同样的常量,具有相同的实体

连接字符串

使用 + 可以连接字符串

1
2
3
String str1 = "good";
String str2 = "student";
String str3 = str1 + str2;

str3 = “goodstudent”

若要连接其它类型,则会在连接之前,先自动调用其它类型的toString方法:

1
2
int time = 2;
System.out.println("我每天用" + time + "小时看书");

输出结果:我明天用2小时看书,这里调用的int型的toString方法

字符串长度

String.length()

字符串查找

String.indexOf(String s)

该方法返回参数字符串s在被查找字符串中首次出现的索引位置,若没找到,返回-1

1
2
3
4
5
6
7
8
String str1 = "We are students";
String str2 = "are";
String str3 = "e";
String str4 = "shit";

System.out.println(str1.indexOf(str2));
System.out.println(str1.indexOf(str3));
System.out.println(str1.indexOf(str4));

输出结果:

1
2
3
3
1
-1

lastIndexOf(String s)

返回参数字符串s最后一次出现的索引位置,若没找到,返回-1

1
2
3
4
5
6
7
8
String str1 = "We are students";
String str2 = "are";
String str3 = "e";
String str4 = "shit";

System.out.println(str1.lastIndexOf(str2));
System.out.println(str1.lastIndexOf(str3));
System.out.println(str1.lastIndexOf(str4));

输出结果:

1
2
3
3
11
-1

获取指定位置索引处的字符

String.charAt(int index)

注意Java不能直接使用数组下标的方式来访问字符串

获取子串

String.substring(int beginIndex)

获取从beginIndex下标处开始,直到字符串结尾的子串

1
2
String str = "Hello world!";
System.out.println(str.substring(3));

运行结果

1
lo world!

String.substring(int beginIndex, int endIndex)

获取从beginIndex开始到endIndex结束的子串

注意:不包括endIndex处的字符

1
2
String str = "Hello world!";
System.out.println(str.substring(1, 3));

运行结果

1
el

去除空格

String.trim()

trim的意思是修剪,String.trim的功能是去除字符串首尾的空格

1
2
String str = "   Hello world!   ";
System.out.println(str.trim());

运行结果

1
Hello world!

字符串替换

String.replace(char oldChar, char newChar)

把字符串中的oldChar字符替换为newChar字符

String.replaceAll(String regex, String replacement)

把字符串中的regex字符串替换为replacement字符串

String.replaceFirst(String regex, String replacement)

把字符串中第一个出现的regex字符串替换为replacement字符串

注意:这里的匹配方式都是正则表达式

1
2
3
4
5
6
String str = "banana!";
System.out.println(str.replace('a', 'e'));
System.out.println(str.replaceAll("an", "fo"));
System.out.println(str.replaceFirst("an", "fo"));
System.out.println(str.replaceFirst(".a", "fo"));
//最后一个的.在正则表达式里面表示任意一个字符,因此可以匹配ba,na

输出结果

1
2
3
4
benene!
bfofoa!
bfoana!
fofofo!

判断开始和结尾

String.startsWith(String prefix)

判断字符串是否是以prefix开始的

String.endsWith(String suffix)

判断字符串是否是以suffix结尾的

1
2
3
4
5
String str = "banana!";
System.out.println(str.startsWith(".a"));
System.out.println(str.startsWith("ba"));
System.out.println(str.endsWith("a."));
System.out.println(str.endsWith("a!"));

结果

1
2
3
4
false
true
false
true

判断字符串相等

*注意:==运算符不能判断字符串是否相等,因为==比较的是字符串的地址

String.equals(String otherstr)

判断字符串是否相等

String.equalsIgnoreCase(String otherstr)

忽略大小写,判断字符串是否相等

按字典序比较

String.compareTo(String otherstr)

若本字符串字典序在otherstr之前,返回负数,相等返回0,否则返回正数

大小写转换

String.toLowerCase()

String.toUpperCase()

字符串分割

String.split(String sign)

按照分隔符sign将字符串分割成字符串数组

String.split(String sign, int limit)

限制分割出的数组长度最大为limit,即匹配limit-1次分隔符,剩下的作为数组的最后一个元素

注意:sign可以是正则表达式

格式化字符串

String.format(String format, Object…args)

是String的静态方法,类似于C++的格式化字符串

1
2
String str = String.format("我的名字是%s", "Peter");
System.out.println(str);

运行结果

1
我的名字是Peter

常见的格式化占位符(只写C++中不同的或不常遇到的):

%b,%B——布尔型

%h,%H——散列码

%%——%

注意:时间和日期类有专门的格式化运算符,之后用到了再说

哈希编码

String.hashCode()

正则表达式

String.matches(String regex)

可以判断是否和正则表达式匹配

常见的正则表达式限定修饰符写法和含义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
.			代表除\n\r以外的任意一个字符
* 对前面的字符或子表达式进行闭包运算,如zo*可以匹配z,zo,zoo……
+ 和*类似,但前面的字符或子表达式至少出现一次,zo+不可以匹配z
? 前面的字符或子表达式出现一次或0次,如z(oo)?可以匹配z和zoo
{n} 前面的字符或子表达式正好出现n次
{n,} 前面的字符或子表达式至少出现n次
{n,m} 前面的字符或子表达式至少出现n次,至多m次
? 当?跟在限定符(*,+,{n,})之后时,表示“非贪心的”,即匹配地越少越好,而不加?则会默认启用“贪心的”模式,匹配极可能长的结果,如ooo会被匹配为一个o+,但会被匹配为一个o+?和一个o
x|y 匹配x或y
xy 匹配xy,连接
[xyz] 匹配xyz中任意一个字符
[^xyz] 匹配除xyz中任意一个字符
- 表示范围,如[0-9]表示0到9这10个字符,[a-z]表示所有小写字母
\b 匹配字边界,比如er\b会和baer中的er匹配,但不会和verb中的er匹配
\B 非字边界
\s 一个空白字符,等效于[ \f\n\r\t\v](注意最前面有个空格)
\S 一个非空白字符,等效于[^ \f\n\r\t\v]
\d 一个数字字符,等效于[0-9]
\D 一个非数字字符,等需要[^0-9]
(?=pattern) 执行正向预测先行搜索的子表达式,该表达式匹配处于匹配 pattern 的字符串的起始点的字符串。它是一个非捕获匹配,即不能捕获供以后使用的匹配。例如,'Windows (?=95|98|NT|2000)' 匹配"Windows 2000"中的"Windows",但不匹配"Windows 3.1"中的"Windows"。预测先行不占用字符,即发生匹配后,下一匹配的搜索紧随上一匹配之后,而不是在组成预测先行的字符后。
(?!pattern) 执行反向预测先行搜索的子表达式,该表达式匹配不处于匹配 pattern 的字符串的起始点的搜索字符串。它是一个非捕获匹配,即不能捕获供以后使用的匹配。例如,'Windows (?!95|98|NT|2000)' 匹配"Windows 3.1"中的 "Windows",但不匹配"Windows 2000"中的"Windows"。预测先行不占用字符,即发生匹配后,下一匹配的搜索紧随上一匹配之后,而不是在组成预测先行的字符后。

注意:根据 Java Language Specification 的要求,Java 源代码的字符串中的反斜线被解释为 Unicode 转义或其他字符转义。因此必须在字符串字面值中使用两个反斜线,表示正则表达式受到保护,不被 Java 字节码编译器解释。例如,当解释为正则表达式时,字符串字面值 “\b” 与单个退格字符匹配,而 “\\b” 与单词边界匹配。字符串字面值 “\(hello\)” 是非法的,将导致编译时错误;要与字符串 (hello) 匹配,必须使用字符串字面值 “\\(hello\\)”。

StringBuilder

直接使用字符串类型String来频繁执行一些操作来创建字符串会耗时较大,StringBuilder加速了这一过程

StringBuilder.append(content)

这里的content可以是char,int,double,String,StringBuilder等,表示在尾部追加

StringBuilder.insert(int offset, arg)

表示在offset下标处插入arg,即offset下标处变为arg的第一个字符

StringBuilder.delete(int start, int end)

删除下标start到下标end之间的字符,即区间[start, end),不删除end处字符

数组

数组的创建

一维数组

一维数组的声明有两种方式:

1
2
int a[];
int[] a;

声明之后,还不能访问其内部,因为声明只给出了数组的类型和名字,真正分配空间的语法如下:

1
a = new int[4];

也可以直接声明和定义放在一起:

1
int a[] = new int[4];

还可以在定义的同时初始化:

1
int a[] = new int[]{1,2,3};

或者直接:

1
int a[] = {1,2,3};

二维数组

声明二维数组的方式和一维数组类似:

1
2
int a[][];
int[][] a;

之后有两种分配内存的方式:

1.直接为每一维分配内存,这样每一维数组的大小一致:

1
a = new int[2][4];

2.分别为每一维分配内存,这样可以分配给不同元素不同的大小空间:

1
2
3
a = new int[2][];
a[0] = new int[3];
a[1] = new int[4];

二维数组也可以在定义的时候初始化:

1
int a[][] = {{1, 2}, {3, 4, 5}};

高维数组以此类推

数组长度

Arrays.length

是成员变量,并非方法

填充替换数组元素

Arrays.fill(int[] a, int value)

可以将int型的数值value赋值给数组的每一个元素

Arrays.fill(int[] a, int fromIndex, int toIndex, int value)

把指定的值value填充数组a下标区间为[fromIndex, toIndex)的区域

排序

Arrays.sort(object)

把object按照从小到大的顺序排序

Arrays.sort(object, int formIndex, int toIndex)

把object下标区间为[fromIndex, toIndex)的区域按从小到大的顺序排序

Arrays.sort(object, int formIndex, int toIndex, Comparator c)

按照比较规则c来为object排序

数组复制

Arrays.copyOf(arr, int newlength)

newlength表示新数组的长度,若小于原数组则从尾部截断,若大于则用0或null等默认值填充

1
2
3
4
5
6
7
8
9
10
11
import java.util.Arrays;

public class Test {
public static void main(String[] args){
int a[] = {1, 2, 3};
int b[] = Arrays.copyOf(a, 7);
for(int i = 0; i < b.length; ++i){
System.out.println(b[i]);
}
}
}

结果

1
2
3
4
5
6
7
1
2
3
0
0
0
0

Arrays.copyOfRange(arr, int fromIndex, int toIndex)

复制原数组区间为[fromIndex, toIndex)处的元素

数组查询

二分查找,要求数组元素可以比较并且有序(从小到大)

Arrays.binarySearch(Object[] arr, Object key)

若key在数组arr中,返回其索引,否则返回“-插入点”,所谓插入点,就是第一个大于key的元素的下标+1

1
2
3
4
5
6
7
8
9
10
11
import java.util.Arrays;

public class Test {
public static void main(String[] args){
int a[] = {1, 3, 5};
System.out.println(Arrays.binarySearch(a,3));
System.out.println(Arrays.binarySearch(a,4));
System.out.println(Arrays.binarySearch(a,6));
System.out.println(Arrays.binarySearch(a,0));
}
}

运行结果

1
2
3
4
1
-3
-4
-1

其中1是3的下标;-3表示第一个比4大的数5的下标是2,加一是3;-4表示没有比6大的数,因此为数组长度+1;-1表示第一个比0小的数是1,下标是0,加一是1

如果把所有的负数返回值取绝对值,然后减一得到一个下标,被查找的元素插入到这个下标上,整个数组还是有序的

Arrays.binarySearch(Object[] arr, int fromIndex, int toIndex, Object key)

在指定的范围[fromIndex, toIndex)之间查找key,若找不到,返回的还是-插入点

Java 包(package)

为了更好地组织类,Java 提供了包机制,用于区别类名的命名空间。

包的作用:

1、把功能相似或相关的类或接口组织在同一个包中,方便类的查找和使用。

2、如同文件夹一样,包也采用了树形目录的存储方式。同一个包中的类名字是不同的,不同的包中的类的名字是可以相同的,当同时调用两个不同包中相同类名的类时,应该加上包名加以区别。因此,包可以避免名字冲突。

3、包也限定了访问权限,拥有包访问权限的类才能访问某个包中的类。

Java 使用包(package)这种机制是为了防止命名冲突,访问控制,提供搜索和定位类(class)、接口、枚举(enumerations)和注释(annotation)等。

包语句的语法格式为:

package pkg1[.pkg2[.pkg3…]];

例如,一个Something.java 文件它的内容

1
2
3
4
package net.java.util;
public class Something{
...
}

那么它的路径应该是 net/java/util/Something.java 这样保存的。 package(包) 的作用是把不同的 java 程序分类保存,更方便的被其他 java 程序调用。

一个包(package)可以定义为一组相互联系的类型(类、接口、枚举和注释),为这些类型提供访问保护和命名空间管理的功能。

以下是一些 Java 中的包:

java.lang-打包基础的类
java.io-包含输入输出功能的函数

开发者可以自己把一组类和接口等打包,并定义自己的包。而且在实际开发中这样做是值得提倡的,当你自己完成类的实现之后,将相关的类分组,可以让其他的编程者更容易地确定哪些类、接口、枚举和注释等是相关的。

由于包创建了新的命名空间(namespace),所以不会跟其他包中的任何名字产生命名冲突。使用包这种机制,更容易实现访问控制,并且让定位相关类更加简单。

创建包

创建包的时候,你需要为这个包取一个合适的名字。之后,如果其他的一个源文件包含了这个包提供的类、接口、枚举或者注释类型的时候,都必须将这个包的声明放在这个源文件的开头。

包声明应该在源文件的第一行,每个源文件只能有一个包声明,这个文件中的每个类型都应用于它。

如果一个源文件中没有使用包声明,那么其中的类,函数,枚举,注释等将被放在一个无名的包(unnamed package)中。

import 关键字

为了能够使用某一个包的成员,我们需要在 Java 程序中明确导入该包。使用 “import” 语句可完成此功能。

在 java 源文件中 import 语句应位于 package 语句之后,所有类的定义之前,可以没有,也可以有多条,其语法格式为:
import package1[.package2…].(classname|*);

如果在一个包中,一个类想要使用本包中的另一个类,那么该包名可以省略。

导入静态成员

导入类的静态成员,之后可以直接使用其成员名字来调用,语法为import后加static

1
import static java.lang.Math.max;

package 的目录结构

类放在包中会有两种主要的结果:

包名成为类名的一部分,正如我们前面讨论的一样。
包名必须与相应的字节码所在的目录结构相吻合。

如上图,类Test在包test中,路径也是在test下有一个Test.Java文件

类和对象

成员变量和成员方法

与C++基本一致

权限修饰符

权限修饰符有private,protected,public三种,与C++差不多,但有区别

修饰类

类也可以被设置权限,对于非内部类来说,类的访问权限只有两种,即public和default(默认),对public的类,可以在任何地方访问它,而默认权限的类只允许在同一个包的内部访问,默认权限不需要特地加修饰符

什么是内部类?

内部类就是类中类,定义在类的内部的成员类

实际演示一下:

1.新建两个包,一个名为test,另一个为othertest

2.test下建两个类,一个名为Test,另一个名为SameTest

3.othertest下建OtherTest类

4.给OtherTest和SameTest两个类都定义一个public的成员变量为a,并将这两个类都设置为public的

5.在Test类中写主函数,导入othertest包,分别输出OtherTest和SameTest的a,发现一切正常

说明public的类无论在任何地方,都可以被访问到

6.去掉OtherTest和SameTest的修饰符public,这时候发现编译器立刻报错提醒,表示OtherTest已经无法被访问,但SameTest还是正常

说明protected的类不能被不同包的其它类访问,但可以被同一个包下的其它类访问

修饰成员

需要注意的是,Java类的权限会影响到成员的权限,比如尽管一个成员变量是public的,但它所属类是默认权限default,那么在不同的包下还是不能访问这个成员变量,这时的成员变量权限也可以看作是default的。

成员变量的访问关系可以由下图所示:

比较有趣的是protected型的成员在不同包下的父子类之间的访问,这里实际上指的是子类的实例可以访问从父类继承而来的protected方法。即在子类中的某个方法fun1()直接访问父类的protected方法,再去实例化子类,通过子类的实例调用fun1(),从而间接实现访问父类的protected方法,但不能通过父类的实例直接去访问其protected方法。

演示:

1.新建两个包,一个名为test,另一个为othertest

2.test下建两个类,一个名为Test,另一个名为SameTest

3.othertest下建OtherTest类

4.在OtherTest下建立protected的函数func,功能是返回-1,并让SameTest继承OtherTest

5.在SameTest里面写一个函数f,功能是返回父类的func函数即return super.func(),并未出现异常

6.在Test的main方法里面建立SameTest的实例s,之后分别调用f和func,发现可以调用f但不能调用func

this关键字

类似于C++的this指针,但Java的this是本对象的引用,所以依然使用.号来访问成员

构造方法

和C++很像,没有返回值,不需要void修饰符,只需要public修饰符,构造方法和类名必须一致

如果没有显示定义构造方法,编译器会自动创建隐式构造方法,隐式构造方法是无参的

析构/垃圾回收

Java有自动的垃圾回收机制,无需手动清理内存,若希望对象能带亡语效果,可以重写Object类的finalize()方法,该方法是protected的,在回收时会先调用它,再回收

但垃圾回收机制不受我们控制,什么时候执行,甚至是否会执行也不清楚,因此可以使用System.gc()来强制进行垃圾回收

静态成员

静态方法或静态变量的作用通常是提供本类共享的一个方法或变量。声明静态成员需要加static关键字。

可以使用类名.静态成员的方式来访问它们,访问它们不一定需要创建实例

在静态方法体中不可以调用非静态方法或者使用this关键字

如果希望执行类时,首先执行类的初始化动作,可以使用static定义一个静态区域,该区域会在该类被使用时首先执行,并且仅仅会执行一次

1
2
3
4
5
public class Book{
static{
// some
}
}

所有类都有一些通用的静态成员,如this,super,new,class,lambda

class.getResource()

获取本类的URL路径

创建实例

使用new关键字

1
2
Book book;
book = new Book();

对象的比较

和String类似,对象的比较不能使用==,因为==是比较对象的地址,比较对象的内容需要使用equals方法

final关键字

final变量

final变量无法改变其值,一般当作常数来使用

若定义了final数组,则数组的内容不能修改

final方法

final方法不能被重写,若方法是private的,那么编译器也会认为它是一个final方法

final类

final类无法被继承

内部类

如果一个类被定义在了另一个类的内部,则称之为内部类

1
2
3
4
5
public class OuterClass{
private class InnerClass{
//...
}
}

如果从外部类中初始化一个内部类对象,那么这个内部类对象就会绑定在该外部类的实例上

内部类可以随意使用外部类的成员,即使它们是private的,内部类使用外部类的成员时直接使用其名字就可以,无需加.,但外部类无法使用内部类的成员

如果想要在外部类的外面实例化一个内部类对象,需要提供一个外部类对象的引用

1
2
OuterClass out = new OuterClass();
InnerClass in = out.new InnerClass();

内部类向上转型为接口

在内部类向上转型成为接口的时候,会隐藏所有内部类的实现特征。这样就可以在一个外部类中实现多个内部类,这些内部类以不同的方式实现了同一个接口,从而使得一个外部类可以有不同的方式相应同一个接口

匿名内部类

1
2
3
4
5
6
7
8
9
10
11
12
13
interface OuterInterface{
}

class OuterClass{
public OuterInterface doit(){
return OuterInterface(){
private int i = 0;
public int getValue(){
return i;
}
};
}
}

这里的doit方法返回的是一个接口

内部类的继承

1
2
3
4
5
public C extends A.B{
public C(A a){
a.super();
}
}

上面的代码中B是A的内部类,C继承了B,这时必须显式地给C的构造函数一个外部类引用参数A,同时调用父类也必须使用a.super语句

包装类和数字处理类

Java是面向对象的语言,希望“一切皆对象”,但对于一些像int,char这样的基本类型,将其看作对象new在堆里会导致运行效率的不理想,因此Java还是使用了基本类型int,char,short,long,byte,float,double,这样可以使效率较高。

但为了符合Java面向对象的理念,为了与其它对象接轨,Java出现了包装类的概念,即把这些基本类型包装起来,使这些基本类型有了对象的性质,为其添加了属性和方法,丰富了其基本操作

Integer

Integer封装的常量

MAX_VALUE

表示最大int

MIN_VALUE

表示最小int

SIZE

表示二进制位数

TYPE

基本类型int的Class实例

数字格式化DecimalFormat

数学运算Math

随机数Random

大数BigInteger和BigDecimal

类的继承

使用关键字extends表示该类继承自某一个类

1
2
3
class B extends A{
//some
}

super

使用super关键字调用父类的成员,子类可以调用父类所有非private的成员

1
2
3
4
5
6
class B extends A{
public B(){
super();//调用父类的构造函数
super.doSomething();//调用父类的doSomething方法
}
}

重写父类方法

将父类方法的名称保留,实现重写其实现方式,更改其存储权限和返回类型等

一般在方法的顶部加上@Override标签,这样编译器会检查是否是一个重写的方法,不加也没关系,但万一写错名字什么的,编译器会认为这是我们为子类添加的新方法

注意方法的修饰权限只能往更大的方向修改,如父类的protected方法可以被修改为protected或public的,但不能被修改成private的

重写返回值类型是J2SE 5.0版本以上的新特性,新的返回值类型只能是旧的返回值类型的子类

实例化子类的对象时,如果子类的构造函数没有显式的调用父类的构造方法,会先调用父类的默认构造方法,再调用子类的构造方法,若有多次继承关系,则会从最顶级的父类开始向下

Object类

Java Object 类是所有类的父类,也就是说 Java 的所有类都继承了 Object,子类可以使用 Object 的所有方法。

方法总结:https://www.runoob.com/java/java-object-class.html

常用的方法:

Object.toString()

如果在自己定义的类中没有覆写直接使用,会输出类的完整类名+@地址

如果想要输出自己希望看到的内容,可以覆盖它

在需要将该类转换为字符串的场景下都会调用它

Object.equals(Object obj)

在说到String的时候,我们说使用==是不能比较两个String是否相等的,==只能比较它们的内存地址是否一致,而想要比较它们的内容的话,需要使用equals,这里的equals就是继承自Object

但我们自己定义的类中没有重写equals方法的话,默认的equals还是会取比较两块内存的内存地址

在Java编程规范中,要求该方法需要有自反性,传递性,对称性和一致性,这也是等价关系的基本特征

Object.hashCode()

返回本对象的哈希编码,要求有一致性,并且若两个对象调用equals返回true,那么它们的hashCode返回值也应该相同

Object.clone()

拷贝本对象,由于是protected类型,必须在自己定义的类中覆盖成public类型才能被调用

同时需要实现Cloneable接口

对象类型的转换

向上转型

子类可以自动地向上转型(隐式类型转换),从而作为父类的对象调用父类的成员方法和成员变量,向上转型总是安全的

向下转型

父类不能使用隐式的类型转换来转为一个子类,为此,我们必须使用显式类型转换将其转为子类

instanceof

执行向下转型时,由于父类实例不一定是子类的实例,可能会产生ClassCastException异常,为了避免这种情况,提前使用instanceof可以判断某个对象是否是属于某个类

1
2
3
if(q instanceof Q){
Q nq = (Q)q;
}

同样的,instanceof也可以判断某个类是否实现了某个接口

方法重载

重载的原则和C++重载一致,不过Java有一种名为不定长参数的语法:

1
2
3
4
5
6
7
public static int add(int...a){
int ret = 0;
for(int i = 0; i < a.length; ++i){
ret += a[i];
}
return ret;
}

这里的add就是使用了不定长参数的方法,实际上编译器会将不定长参数a看作数组,可见的是函数体里也使用了数组的成员length

这样,调用add时可以使用任意个int型的参数

多态

利用多态可以使程序具有良好的扩展性,并可以对所有的类对象做通用的处理

抽象类

抽象类的关键字是abstract

1
2
3
public abstract class Test{
abstract void testAbstract();
}

只要类中有一个抽象方法,这个类必须声明为抽象类

抽象类不能实例化对象,只能被继承,所有继承了抽象类的类必须实现其所有抽象方法,否则也必须声明自己是抽象类

接口

接口是纯粹的抽象类,其中所有方法都没有方法体

接口允许多继承

假设我们有员工类和账单类,我们希望它们都能实现一个方法,该方法把它们的一些信息发送到某个公司的网络上,没有接口的话或许我们就要给两个毫不相干的类搞一个公共的父类了,但我们有了接口,就可以让它们实现某个相同的接口

同样的,每个类都可以根据需要实现多个接口,比如某个类的父类有一个抽象方法,但子类不一定用得到,这时候不妨把该抽象方法设为一个接口,让需要的子类实现它

创建接口的方法是使用interface关键字

1
2
3
public interface drawTest{
void draw();
}

在接口中的方法都是public和abstract的,其它的修饰符不会通过编译,所以可以不需要显示说明方法是public和abstract。直接省略它们

但接口本身可以是default的

使用implements关键字继承接口

1
2
3
class A extends B implements i1, i2, i3, ..., in{
//some
}

在继承了接口的类中必须实现接口中的方法

当不同的类实例需要调用同一个接口,也可以使用对象数组进行向上转型,使其成为接口类,这和抽象类是一样的

异常处理

语法

1
2
3
4
5
6
7
8
9
10
11
try{
//程序代码块
}catch(Exceptiontype1 e){
//处理异常1
}catch(Exceptiontype2 e){
//处理异常2
}
...
finally{
//程序块
}

程序在try中运行时一旦出现异常,就会推出try转到该异常对应的catch语句中处理。若没有出现异常,则执行完try中的语句后正常退出,无论try中是否出现了异常,程序都会执行finally代码块

常见的处理方式

Exception.getMessage()

输出错误的性质

Exception.toString()

输出错误的性质和类型

Exception.printStackTrace()

输出异常的性质、类型和栈层次以及出现在程序中的位置

常见异常

https://www.cnblogs.com/cvst/p/5822373.html

自定义异常

自定义的异常类要继承Exception类

Exception类继承自Throwable,有一个detailMessage的属性可以记录异常信息

我们自定义的异常类可以使用我们自定义的异常信息作为提示

在方法中抛出异常

throws

在方法的定义后使用throws关键字声明该方法可能抛出的所有异常

所有调用该方法的地方必须处理好该方法可能抛出的所有异常

throw

可以使用throw语句后接一个异常类的对象,抛出异常

异常的继承关系

如果父类的方法抛出了多个异常,子类覆盖该方法时,只能抛出父类可能抛出异常的子集或子类集,不能抛出更多的异常

AWT

AWT指的是“抽象窗口工具集”,当我们使用AWT的组件时,JVM会调用相应操作系统的API去实现。

比如同样的一段程序,我们放在Windows上去运行,运行出的风格就是Windows的风格,放在Linux下,就是Linux的风格

继承体系

Component代表能够显现出来,并与用户交互的对象

MenuComponent代表菜单

Container是特殊的组件,意思为“容器”,表示它们可以包含其它的组件

还有一个接口名字加LayoutManager,是布局管理器

学习AWT主要就是学习这三部分

Component

Component作为所有组件的基类,常用的API如下:

setLocation(int x, int y)

设置位置,左上角是原点,x是横向,y是纵向,单位是像素

setSize(int width, int height)

设置大小

setBounds(int x, int y, int width, int height)

同时设置组件的位置大小

setVisiable(boolean b)

设置可见

Container

Container继承体系:

Window是可以独立存在的顶级窗口,默认使用BorderLayout来管理其布局

Panel可以容纳其它组件却不能独立存在,它必须内嵌在其它容器中使用,默认使用FlowLayout管理其布局

ScrollPane也不能独立存在,默认使用BorderLayout管理布局

常用方法

add(Component c)

向当前组件中添加组件,返回被添加的组件

getComponentAt(int x, int y)

返回指定点(x, y)处的组件

getComponentCount()

返回当前容器中有多少个组件

getComponents()

获取所有的组件,返回一个Component数组

演示1:创建一个窗口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package test;


import java.awt.*;

public class Test {
public static void main(String[] args){
//1.创建窗口对象
Frame frame = new Frame("这是一个窗口测试");
//2.设置其大小,位置
frame.setBounds(100, 100, 500, 300);
//3.使其可见
frame.setVisible(true);
}
}

演示2:创建一个Panel

Swing

Swing 是一个为Java设计的GUI工具包。

Swing是JAVA基础类的一部分。

Swing包括了图形用户界面(GUI)器件如:文本框,按钮,分隔窗格和表。

Swing提供许多比AWT更好的屏幕显示元素。它们用纯Java写成,所以同Java本身一样可以跨平台运行,这一点不像AWT。它们是JFC的一部分。它们支持可更换的面板和主题(各种操作系统默认的特有主题),然而不是真的使用原生平台提供的设备,而是仅仅在表面上模仿它们。这意味着你可以在任意平台上使用JAVA支持的任意面板。轻量级组件的缺点则是执行速度较慢,优点就是可以在所有平台上采用统一的行为。

Swing组件中最重要的父类是Container类,即Java.awt.Container,它有两个最重要的子类,Java.awt.Window和Java.awt.Frame,Swing组件扩展了这两个类,Java关于窗口组件的编写,都与组件和容器的概念相关联

Window:
平时我们看到的各种各样的应用程序的窗口都可以称为Window,
Window作为一个应用程序窗口独立显示出来。

Pannel:
Pannel不能作为应用程序的独立窗口显示出来,
Pannel要想显示出来就必须得把自己装入到Window里面才能显示出来。

Swing组件采用MVC设计模式

常用Swing组件

JButton 按钮

JCheckBox 复选框

JComBox 下拉列表框

JFrame 框架

JDialog 对话框

JLabel 标签

JRadioButtom 单选按钮

JList 在用户界面中显示一系列条目的组件

JTextField 文本框

JPasswordField 密码框

JTextArea 文本区域

JOptionPane 对话框

窗体

JFrame窗体

JFrame窗体是一个容器,它是Swing程序中各个组件的载体,可以将JFrame看作是承载这些组件的容器

开发时,可以继承javax.swing.JFrame创建自己的窗体,由于继承了javax.swing.JFrame,新的窗体有最大化,最小化,关闭三个按钮

构造

JFrame()

JFrame(String title)

分别是无参的构造方法和有参的构造方法第一种形式可以创建一个初始不可见,无标题的新窗体,第二种创建一个初始不可见,但有标题的新窗体

使窗体可见

setVisible(true)

调整大小

setSize(int x, int y)

设置窗体关闭方式

setDefaultCloseOperation()

其中可以选的参数有:

DO_NOTHING_ON_CLOSE 关闭窗体什么都不做

DISPOSE_ON_CLOSE 注册监听程序对象后会自动隐藏并释放窗体

HIDE_ON_CLOSE 隐藏窗口的默认窗口关闭

EXIT_ON_CLOSE 退出应用程序时默认窗口关闭

为窗体添加组件

getContentPane()

返回一个Container型的对象,将窗体转换为容器

使用容器的add方法就可以向里面添加组件,使用remove方法可以移除组件

使用remove或add后,要使用reprint方法来重新绘制窗体

关于Container:https://docs.oracle.com/javase/8/docs/api/java/awt/Container.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package test;

import javax.swing.*;
import java.awt.*;

public class Test {
public static void main(String[] args){
//新建一个JFrame窗体
/*
JFrame jf = new JFrame();
jf.setSize(400, 300);
Container container = jf.getContentPane();
*/
//新建一个标签并添加进窗体
JLabel label1 = new JLabel("这是一个标签");
container.add(label1);
model.setVisible(true);
//删除该标签
model.remove(label1);
model.repaint();
}
}

JDialog窗体

JDialog窗体是Swing组件中的对话框,它继承了AWT组件中的java.awt.Dialog类

其功能是从一个窗体中弹出另一个窗体

同样需要使用getContentPane()转换成为容器之后才能向内部添加组件

构造

JDialog()

JDialog(Frame f, String title, boolean model)

创建一个父窗体为f,标题为title,类型为model的对话框

标签组件和图标

标签的使用

标签由JLable定义,父类为JComponent类

标签只可以显示一行只读文本,一个图像或者带图像的文本,不能产生任何类型的事件

构造

JLable()

JLable(String text, Icon icon, int aligment)

创建一个带文本,带图标的标签,并设定其对齐方式

对齐方式在SwingConstants中可以找到

图标的使用

图标可以放置在按钮,标签等组件上,用于描述组件的用途,图标可以使用Java支持的文件类型创建,也可以使用java.awt.Graphics类提供的方法创建

创建

通过Icon接口创建图标,可以在创建时给定图标的大小,颜色等特性,使用Icon接口需要实现其三个方法:

int getIconHeight()

int getIconWidth()

void paintIcon(Component arg0, Graphics arg1, int arg2, int arg3)

自己实现一个DrawIcon类实现了Icon接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package test;

import javax.swing.*;
import java.awt.*;

public class DrawIcon implements Icon {
private int height;
private int width;

public int getIconHeight(){
return this.height;
}

public int getIconWidth(){
return this.width;
}

public DrawIcon(int height, int width){
this.height = height;
this.width = width;
}

public void paintIcon(Component arg0, Graphics arg1, int x, int y){
arg1.fillOval(x, y, width, height);
}
}

试运行代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package test;

import javax.swing.*;
import java.awt.*;
import java.util.Scanner;
import test.DrawIcon;

public class Test {
public static void main(String[] args){
DrawIcon icon = new DrawIcon(15, 15);
JLabel j = new JLabel("测试", icon, SwingConstants.CENTER);
JFrame jf = new JFrame("我爱你");
jf.setSize(300, 300);
Container c = jf.getContentPane();
c.add(j);
jf.setVisible(true);
}
}

这里注意到绘制图标的printIcon函数从未调用过,实际上在绘制窗体时被内部调用。只需要把图标加进标签里就好了。

使用图片图标

javax.swing,ImageIcon类可以使用现有的图片创建图标,它是一个实现了Icon接口的类

创建

ImageIcon()

创建一个通用的ImageIcon对象,真正需要设置图片的时候再使用setImage(Image image)方法

ImageIcon(Image image)

直接用图片创建图标

ImageIcon(Image image, String description)

用图片创建图标之外,还可以创建简短的描述,使用getDescription方法来取得这个描述

ImageIcon(URL url)

使用URL路径创建图标

集合

Collection接口

Collection接口是层次结构的根接口,该接口不能直接使用,但提供了添加元素,删除元素,管理数据的方法

Set和List集合都实现了Collection接口,所以这些方法对它们都是通用的:

add(E e)

remove(Object o)

isEmpty()

iterator()

size()

迭代器Iterator

使用集合的iterator方法可以返回一个Iterator型的对象,主要方法有以下几个:

next()

第一次调用时返回第一个元素,再次调用返回下一个元素,返回的对象类型是Object

hasNext()

判断是否有下一个元素

remove()

删除刚刚返回过的元素

List集合

List接口的常用实现类有ArrayList和LinkedList,一个是顺序表,一个是链表

1
2
List<E> list1 = new ArrayList<>();
List<E> list2 = new LinkedList<>();

Set集合

常用实现类有HashSet和TreeSet

HashSet有哈希表实现,TreeSet使用二叉树实现,要装进TreeSet,需实现Comparable接口

TreeSet常用方法

first()

第一个,最低元素

last()

最后一个,最高元素

comparator()

返回比较器,若使用的是自然比较器则返回null

headSet(E toElement)

返回[first, toElement)这个集合

subSet(E fromElement, E toElement)

返回[fromElement, toElement)这个集合

tailSet(E fromElement)

返回[fromElement, last]这个集合

实现Comparable接口

1
2
3
4
5
6
7
8
9
10
public class UpdateStu implements Comparable<Object> {
String name;
long id;

public int compareTo(Object o){
UpdateStu upstu = (UpdateStu) o;
int result = id > upstu.id ? 1 : (id == upstu.id ? 0 : -1);
return result;
}
}

Map集合

Map没有实现Collection接口,提供的是key到value的映射

常用实现类有TreeMap和HashMap,TreeMap关于key有序,而HashMap允许null值和null键,但TreeMap不允许null键

put(key, value)

添加key,value对

containsKey(key)

是否包含key

containsValue(value)

是否包含value

get(key)

取key值,不存在则null

keySet()

取key值的集合

values()

取value集合

输入输出流

InputStream

抽象类InputStream是所有字节输入流的父亲,该类中所有方法遇到错误时都会引发IOException异常

常用方法

read()

从输入流中读取数据的下一个字节,返回0~255范围内的int字节值,如果因为已经读到流的末尾而没有读到可用的自己,返回-1

该方法是抽象的,所有继承了InputStream的类都需要重写该方法

read(byte[] b)

从输入流中读取一定长度的字节到数组b中,并以整数的方式返回字节数

mark(int readlimit)

在输入流的当前位置放一个标记,readlimit参数告知此输入流在标记位置失效之前允许读取的字节数,和reset配合使用

reset()

将输入指针返回到mark处

例:

1
2
3
4
5
6
reader.mark(50);
int a = reader.read();
int b = reader.read();
reader.reset();
int c = reader.read();
int d = reader.read();

那么肯定有a = c,b = d

skip(long n)

跳过流上的n个字符,并返回实际跳过的字符数目

markSupported()

如果当前流支持mark和reset方法就返回true

close()

关闭流,释放和该流相关的所有系统资源

Reader

Reader是处理字符文本的,为什么有一个InputStream,又要有一个Reader呢?是因为Java中的文本是双字节,所以为文本输入提供了一套单独的类

所有处理字符文本的类都是抽象类Reader的子类

其方法与InputStream类似

OutputStream

输出字节的抽象类,其所有方法都是void,出错时都会抛出IOException异常

常用方法

write(int b)

write(byte[] b)

write(byte[] b, int off ,int len)

flush()

彻底完成输出并清空缓冲区

Writer

类比Reader

File类

定义了一些与平台无关的方法来操作文件,通过使用File类中的方法,可以实现文件的创建,删除,重命名等操作

创建

File(String pathname)

通过路径名,创建一个新的File实例

通过exists()方法可以判断这个文件是否存在

若不存在,可以使用createNewFile()方法创建

若存在,可以使用delete()方法删除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package test;


import java.io.File;

public class Test {
public static void main(String[] args){
File file = new File("test.txt");
if(file.exists()){
file.delete();
System.out.println("delete file.");
}else{
try{
file.createNewFile();
System.out.println("create file.");
} catch (Exception e){
System.out.println(e.getMessage());
}
}
}
}

上面的代码,第一次运行时,由于没有该文件,所以创建了文件。再次运行时,由于检测到了文件存在,所以删除文件。

默认文件创建在当前工程目录下,如果想要在别的目录下创建,可以使用路径

File(String parent, String child)

利用指定的父子路径创建文件,如parent=”D:/“,child=”letter.txt”

File(File f, String child)

利用父子路径创建文件,这里的f是父路径对象

mkdir()

mkdirs()

boolean mkdir() : 创建此抽象路径名指定的目录。
boolean mkdirs() : 创建此抽象路径名指定的目录,包括创建必需但不存在的父目录。

获取文件本身信息

getName()

获取文件的名称

canRead()

是否可读

canWrite()

是否可写

exits()

是否存在

length()

文件长度,以字节为单位

getAbsolutePath()

获取绝对路径

getParent()

获取文件的父路径

isFile()

是不是文件

isDirectory()

是不是文件夹

isHidden()

是不是隐藏文件

lastModified()

文件最后修改时间,返回long类型

listFiles()

返回目录中的子文件

文件输入输出流

FileInputStream和FileOutPutStream类

构造方法

FileInputStream(String name)

FileInputStream(File file)

FileOutStream也有和上面一模一样的构造方法,而且,FileOutStream可以指定不存在的文件名,但该文件不能是已经被另一个程序打开的文件

主要方法继承自InputStream和OutputStream

FileReader和FileWriter类

方法类似上面,但主要是处理文本字符

带缓存的输入/输出流

BufferedInputStream和BufferedOutputStream

构造

BufferedInputStream(InputStream in, int size=32)

把InputStream类in对象包装,默认缓冲区大小为32字节

flush()

用于BufferOutputStream,强制把缓冲区的内容输出到外设

BufferedReader和BufferedWriter类

由于有了缓存,可以支持更多操作

readLine()

读取一行字符返回为String,若无数据返回null

write(String s, int off, int len)

写入字符串的某一部分

newLine()

写入一个行分隔符

数据输入输出流

DataInputStream和DataOutputStream允许应用程序以与机器无关的方式从底层输入流中读取Java基本数据类型

构造

DataInputStream(InputStream in)

DataOutputStream(OutputStream out)

常用方法

writeBytes(String s)

把字符串中每一个字符的低字节内容写入

writeChars(String s)

把字符串每一个字符的两个字节内容都写入

writeUTF(String s)

先写入UTF编码后的字符串长度,再把编码后的字符串写入

readUTF()

读字符串

ZIP压缩读入/输出

ZipOutputStream和ZipInputStream可以实现文件的压缩和解压缩

压缩

构造

ZipOutputStream(OutputStream out)

常用方法

putNextEntry(ZipEntry e)

开始写一个新的ZipEntry,并将流内的位置移至此entry所指数据的开头

write(byte[] b, int off, int len)

将字节数组写入当前ZIP条目数据

finish()

完成写入ZIP输出流的内容,无需关闭其OutputStream

setComment(String comment)

设置ZIP文件的注释文字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package test;


import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class Test {
public void zip(String zipName, File inputFile) throws Exception{
ZipOutputStream zipOutputStream = new ZipOutputStream(new FileOutputStream(zipName));
zip(zipOutputStream, inputFile, "");
System.out.println("压缩中……");
zipOutputStream.close();
}

public void zip(ZipOutputStream out, File f, String base) throws Exception {
System.out.println(base);
if(f.isDirectory()){
File[] fl = f.listFiles();
if(base.length() != 0){
out.putNextEntry(new ZipEntry(base + "/"));
}
for(int i = 0; i < fl.length; ++i){
zip(out, fl[i], fl[i].toString());
}
}else{
out.putNextEntry(new ZipEntry(base));
FileInputStream in = new FileInputStream(f);
int b;
while((b = in.read()) != -1){
out.write(b);
}
in.close();
}
}

public static void main(String[] args){
Test a = new Test();
try {
a.zip("my.zip", new File("压缩测试"));
System.out.println("压缩完成");
}catch (Exception e){
System.out.println(e.getMessage());
}
}
}

解压缩

构造

ZipInputStream(InputStream in)

常用方法

read(byte[] b, int off, int len)

读取目标数组b内偏移为off,长度为len字节

available()

判断是否已经读完当前entry的数据,若是则返回0,否则返回1

closeEntry()

关闭当前ZIP条目,并定位流读取下一个条目

skip(long n)

跳过当前ZIP条目中指定的字节数

getNextEntry()

读取下一个Entry,并将流内的位置移至该entry所指向的数据的开头

createZipEntry(String name)

以指定的name参数创建一个新的ZipEntry对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package test;


import java.io.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

public class Test {
public static void main(String[] args){
File file = new File("my.zip");
ZipInputStream zipInputStream;
try{
ZipFile zipFile = new ZipFile(file);
zipInputStream = new ZipInputStream(new FileInputStream(file));
ZipEntry zipEntry;
while((zipEntry = zipInputStream.getNextEntry()) != null){
if(zipEntry.isDirectory()) continue;
File tmp = new File("D:\\" + zipEntry.getName());
System.out.println(tmp.toString());
if(!tmp.exists()){
tmp.getParentFile().mkdirs();
OutputStream os = new FileOutputStream(tmp);
InputStream in = zipFile.getInputStream(zipEntry);
int count = 0;
while((count = in.read()) != -1){
os.write(count);
}
os.close();
in.close();
}
zipInputStream.closeEntry();
System.out.println("解压成功");
}
zipInputStream.close();
}catch (Exception e){
System.out.println(e.getMessage());
}
}
}

这本Java从入门到精通属实垃圾,给的样例代码竟然是错的,我操他大爷的

多线程

Thread类

Thread类是java.lang包中的一个类,从这个类中实例化的对象代表线程。

构造

Thread()

Thread(String threadName)

执行

run()

启动时,使用start()方法

1
2
3
4
5
6
7
8
9
10
11
12
13
package test;

public class ThreadTest extends Thread{
private int count = 1000;
public void run(){
while(true){
System.out.println(count);
if(--count == 0){
return;
}
}
}
}

Main中这样写:

1
2
3
4
5
6
public static void main(String[] args){
ThreadTest test1 = new ThreadTest();
ThreadTest test2 = new ThreadTest();
test1.start();
test2.start();
}

能看到两个线程交替输出的情形

Runnable接口

一个已经继承自其它类的类无法再继承Thread类,这时候可以通过继承Runnable接口来实现线程的功能

继承Runnable接口主要需要实现run()方法

操作线程