- java基础知识总结(初稿)
- 前言:
- 1、IDEA常用的快捷键
- 2、基本的dos命令
- 一、java入门
- 1.1、java概述:
- 1、java历史和架构
- 2、jdk和jre介绍
- 3、jdk的安装和环境的配置
- 1.2、java的基础语法:
- 1、基本语法:
- 2、Java标识符:
- 3、Java修饰符:
- 4、Java的基本数据类型:
- 5、常量与变量:
- 6、运算符 :
- 7、循环结构
- while 循环:
- do.while循环:
- for循环:
- 关键字:
- 8、条件语句与switch case:
- 9、引用的数据类型(数组,类,接口):
- 10、包机制
- 11、javadoc(JDK帮助文档)
- 12、方法:
- 二、面向对象
- 1、对象
- 继承: 子类继承父类的属性和方法
- 方法发的重写(Override)与重载(Overload)
- 多态:
- 封装(该漏的漏,该藏得藏):
- 2、类
- 3、常用类
- (1)Number&Math类:
- (2)Math类:
- (3)Object类
- (4)Character 类
- (5)String类与StringBuffer类
- 1、String类
- 2、StringBuffer类
- (6)日期类
- 1、date类
- 2、输出日期方式:
- 3、Calendar类:
- 4、Scanner类(键盘输入):
- (7)BigDecimal类
- 4、接口
- 1、介绍:
- 2、接口与类相似点:
- 3、接口特性
- 4、接口的声明
- 5、接口的实现
- 三、Java 流(Stream)、文件(File)和IO
- 1、Java IO流的概念:
- 2、分类
- (1)按照操作单元划分,可以划分为字节流和字符流。
- 四、异常
- 1、异常分类:
- 2、类的层次:
- 3、捕获异常
- 五、集合
- 1、简介:
- 2、Collection接口(单列集合)
- 2.1 List接口:
- (1)是什么?
- (2)ArrayList实现类
- (3)linkedList实现类
- (4)Vector实现类
- 2.2 Set接口、
- (1)是什么:
- (2)HashSet实现类
- (3)TreeSet实现类(不常用)
- 2.3 Set和List的区别:
- 3、Map接口(双列集合)
- 3.1 介绍:
- 3.2 HashMap实现类(面试常问点)
- 3.3 HashTable实现类(基本不用,不做解释)
- 4、集合的遍历
- 4.1遍历 ArrayList
- 4.2 遍历 Map
- 5、迭代器(Iterator)
- 六、多线程
- 1、线程和进程
- 1.1 什么是进程,什么是线程:
- 1.2 Java中的多线程:
- 2、Java多线程编程
- 2.1 一个线程的生命周期:
- 2.2 线程的优先级
- 2.3 线程的创建
- (1)通过实现 Runnable 接口来创建线程:
- (2) 通过继承Thread来创建线程
- (3)通过 Callable 和 Future 创建线程
- 2.4创建线程的三种方式的对比
2、基本的dos命令main函数:输入psvm后按Tab键(本人通常输入main后tab)
System.out.println():输入sout后按Tab键
Ctrl+Shift+N 查找文件
Ctrl+Alt+L 格式化代码(调整缩进)
Ctrl+Alt+O 优化导入的类和包
Alt+/ 自动补全代码 注:默认与输入法有冲突,在setting->keymap->main menu->code->complete code->basic
Ctrl+P 方法参数提示
Ctrl+X 删除行
Ctrl+D 复制行 复制当前行到下一行
Ctrl+H 显示类结构图
Ctrl+Q 显示注释文档
Alt+1 快速打开或隐藏工程面板
F2 或Shift+F2 高亮错误或警告快速定位
代码标签输入完成后,按Tab,生成代码。
选中文本,按Ctrl+Shift+F7 ,高亮显示所有该文本,按Esc高亮消失。
Ctrl+W 选中代码,连续按会有其他效果
选中文本,按Alt+F3 ,逐个往下查找相同文本,并高亮显示。
Ctrl+Up/Down 光标跳转到第一行或最后一行下
Ctrl+B 快速打开光标处的类或方法
一、java入门 1.1、java概述: 1、java历史和架构打开cmd win+r cmd 在文件夹路径加cmd打开命令行
shift+右键 powershell
盘符切换 E:
查看当前目录下所有文件 dir
切换目录 cd /d实现跨盘符切换 cd…上一级
清空 cls clear screen
退出终端exit
查看ipconfig
打开应用 calc mspaint notepad
ping
创建文件夹 md test
创建文件 cd>a.txt
删除 del
移除 rd test
- 1991 年Sun公司的James Gosling(詹姆斯·高斯林)等人开始开发名称为 Oak 的语言,希望用于控制嵌入在有线电视交换盒、PDA等的微处理器;1994年将Oak语言更名为Java
Java的三种技术架构:
- JAVAEE: Java Platform Enterprise Edition,开发企业环境下的应用程序,主要针对web程序开发;
- JAVASE: Java Platform Standard Edition,完成桌面应用程序的开发,是其它两者的基础;
- JAVAME: Java Platform Micro Edition,开发电子消费产品和嵌入式设备,如手机中的程序
2、jdk和jre介绍
- JDK:Java Development Kit,java的开发和运行环境,java的开发工具和jre。
- JRE:Java Runtime Environment,java程序的运行环境,java运行的所需的类库+JVM(java虚拟机)。
- JVM:Java Virtual Machine,它是java运行环境的一部分,是一个虚构出来的计算机,它是通过在实际的计算机上仿真模拟各种计算机功能来实现的。JVM是用来解析和运行Java程序的。
3、jdk的安装和环境的配置
jdk的安装:
下载地址:jdk自选版本;
安装地址:默认的地址是C盘的programe里的Java文件夹,可以自定义安装地址(不影响);
环境配置:
系统环境配置:变量名:JAVA_HOME 变量值:jdk的安装目录。
path环境配置:path=%JAVA_HOME%bin
classpath环境配置:变量名:CLASSPATH 变量值:.;%JAVA_HOME%libtools.jar;%JAVA_HOME%libdt.jar;
问题: 1、为什么要配置path变量? 因为电脑系统将根据该变量的值找到java编程中需要的一些程序,比如javac.exe、java.exe、javah.exe等等,其中javac.exe程序是用于编译java源代码,java.exe程序是用于执行后缀为class的代码。 2、为什么要配置classpath变量? 配置classpath变量,才能使得java解释器知道到哪里去找标准类库,这些标准类库是别人已经写好了的,我们只管使用。比如我们常用到java.lang包中的类,在配置classpath变量后被设为默认导入,所以在写程序时就不用import这个包了。
4,javac命令和java命令做什么事情呢?
- 要知道java是分两部分的:一个是编译,一个是运行。
- javac:负责的是编译的部分,当执行javac时,会启动java的编译器程序。对指定扩展名的.java文件进行编译。生成了jvm可以识别的字节码文件。也就是class文件,也就是java的运行程序。
- java:负责运行的部分.会启动jvm.加载运行时所需的类库,并对class文件进行执行.
一个文件要被执行,必须要有一个执行的起始点,这个起始点就是main函数.
- 大小写敏感:Java 是大小写敏感的,这就意味着标识符 Hello 与 hello 是不同的。
- 类名:对于所有的类来说,类名的首字母应该大写。
- 方法名:所有的方法名都应该以小写字母开头。如果方法名含有若干单词,则后面的每个单词首字母大写。
- 源文件名:源文件名必须和类名相同。当保存文件的时候,你应该使用类名作为文件名保存(切记 Java 是大小写敏感的),文件名的后缀为 .java。(如果文件名和类名不相同则会导致编译错误)。
- 主方法入口:所有的 Java 程序由 public static void main(String[] args) 方法开始执行。
- Java 所有的组成部分都需要名字。类名、变量名以及方法名都被称为标识符。
3、Java修饰符:注意点:
所有的标识符都应该以字母(A-Z 或者 a-z),美元符($)、或者下划线(_)开始
首字符之后可以是字母(A-Z 或者 a-z),美元符($)、下划线(_)或数字的任何字符组合
关键字不能用作标识符
标识符是大小写敏感的
合法标识符举例:age、$salary、_value、__1_value
非法标识符举例:123abc、-salary
-
像其他语言一样,Java可以使用修饰符来修饰类中方法和属性。主要有两类修饰符:
访问控制修饰符 : default, public , protected, private
非访问控制修饰符 : final, abstract, static, synchronized 等
(1)访问控制修饰符:
-
default (即默认,什么也不写): 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。
-
private : 在同一类内可见。使用对象:变量、方法。
-
public : 对所有类可见。使用对象:类、接口、变量、方法
-
protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。
-
注意:
父类中声明为 public 的方法在子类中也必须为 public。
父类中声明为 protected 的方法在子类中要么声明为 protected,要么声明为 public,不能声明为 private。
父类中声明为 private 的方法,不能够被继承。
(2)非访问控制符:
-
static 修饰符:
**静态变量:**static 关键字用来声明独立于对象的静态变量,无论一个类实例化多少对象,它的静态变量只有一份拷贝。 静态变量 也被称为类变量。局部变量不能被声明为 static 变量。
**静态方法:**static 关键字用来声明独立于对象的静态方法。静态方法不能使用类的非静态变量。静态方法从参数列表得到数据, 然后计算这些数据。
-
final 修饰符
-
final变量:表示"最后的、最终的"含义,变量一旦赋值后,不能被重新赋值。被 final 修饰的实例变量必须显式指定初始值。
-
final 方法:父类中的 final 方法可以被子类继承,但是不能被子类重写。声明 final 方法的主要目的是防止该方法的内容被修改。
-
final 类:final 类不能被继承,没有类能够继承 final 类的任何特性。
-
abstract 修饰符:
**抽象类:**抽象类不能用来实例化对象,声明抽象类的唯一目的是为了将来对该类进行扩充。
抽象方法:抽象方法是一种没有任何实现的方法,该方法的的具体实现由子类提供。
如果一个类包含若干个抽象方法,那么该类必须声明为抽象类。抽象类可以不包含抽象方法。
抽象方法的声明以分号结尾,例如:public abstract sample();
-
synchronized 修饰符:声明的方法同一时间只能被一个线程访问。
-
transient 修饰符:序列化的对象包含被 transient 修饰的实例变量时,java 虚拟机(JVM)跳过该特定的变量,该修饰符包含在定义变量的语句中,用来预处理类和变量的数据类型。
-
volatile 修饰符:volatile 修饰的成员变量在每次被线程访问时,都强制从共享内存中重新读取该成员变量的值。而且,当成员变量发生变化时,会强制线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。
-
Java语言提供了八种基本类型。六种数字类型(四个整数型,两个浮点型),一种字符类型,还有一种布尔型。
-
byte: 数据类型是8位、有符号的,以二进制补码表示的整数;
最小值是 -128(-2^7)------ 最大值是 127(2^7-1) ------ 默认值为0;
byte 类型用在大型数组中节约空间,主要代替整数,因为 byte 变量占用的空间只有 int 类型的四分之一;
-
short: 数据类型是 16 位、有符号的以二进制补码表示的整数
最小值是 -32768(-2^15)— 最大值是 32767(2^15 - 1)默认值是 0;
Short 数据类型也可以像 byte 那样节省空间。一个short变量是int型变量所占空间的二分之一;
-
int: 数据类型是32位、有符号的以二进制补码表示的整数
最小值是 -2,147,483,648(-2^31)---- 最大值是 2,147,483,647(2^31 - 1)默认值是 0 ;
一般地整型变量默认为 int 类型;
-
long: 数据类型是 64 位、有符号的以二进制补码表示的整数
最小值是 -9,223,372,036,854,775,808(-2^63)— 最大值是 9,223,372,036,854,775,807(2^63 -1)默认值是 0L;
这种类型主要使用在需要比较大整数的系统上;
"L"理论上不分大小写,但是若写成"l"容易与数字"1"混淆,不容易分辩。所以最好大写。
-
float: 数据类型是单精度、32位、符合IEEE 754标准的浮点数
float 在储存大型浮点数组的时候可节省内存空间;
浮点数不能用来表示精确的值,如货币;一般货币表示使用BigDecimal。
默认值是** 0.0f**;
-
double: 数据类型是双精度、64 位、符合 IEEE 754 标准的浮点数(特点和float一样)
默认值是 0.0d;
-
**Boolean:**数据类型表示一位的信息;
只有两个取值:true 和 false,这种类型只作为一种标志来记录 true/false 情况,默认值是 false;
-
char: 是一个单一的 16 位 Unicode 字符;
最小值是 u0000(十进制等效值为 0)---- 最大值是 uffff(即为 65535)
char 数据类型可以储存任何字符例子:char letter = ‘A’;。
默认值:‘u0000’(空)
重点理解转义字符
-
-
注意:
- byte、int、long、和short都可以用十进制、16进制以及8进制的方式来表示。当使用字面量的时候,前缀 0 表示 8 进制,而前缀 0x 代表 16 进制
- 自动类型转换:转换从低级到高级,byte,short,char—> int —> long—> float —> double
- 浮点数到整数的转换是通过舍弃小数得到,而不是四舍五入。
- 从高到底转换数据类型会导致益处。
- 不能把对象类型转换成不相关类的对象。
-
常量:是在程序中的不会变化的数据。
- 虽然常量名也可以用小写,但为了便于识别,通常使用大写字母表示常量。
- 在 Java 中使用 final 关键字来修饰常量。
-
变量:其实就是内存中的一个存储空间,用于存储常量数据。
-
变量类型:
类变量(全局变量或静态变量):独立于方法之外的变量,用 static 修饰。
实例变量:独立于方法之外的变量,不过没有 static 修饰。
局部变量:类的方法中的变量。
-
变量的作用域: 作用域从变量定义的位置开始,到该变量所在的那对大括号结束;
-
-
命名规范:
- 见名知意
- 驼峰命名
- 类名大写
- 常量:大写+下划线
- 不要使用拼音命名。
基于菜鸟详细教程
- 算术运算符:+ - * / % ++ --等等
- 赋值运算符:=
- 关系运算符:> < >= <= == != instanceof
- 逻辑运算符:&& || !
- 位运算符:& ^ | ~ << >> >>> <<<
- 条件运算符:? :(三目运算符)
- 扩展运算符:+= -= *= /=
- while 循环:
- 只要布尔表达式为 true,循环就会一直执行下去。
while( 布尔表达式 ) { //循环内容 } - do.while循环:
- do…while 循环和 while 循环相似,不同的是,do…while 循环至少会执行一次。
do { //代码语句 }while(布尔表达式); - for循环:
- 最先执行初始化步骤。可以声明一种类型,但可初始化一个或多个循环控制变量,也可以是空语句。
- 然后,检测布尔表达式的值。如果为 true,循环体被执行。如果为false,循环终止,开始执行循环体后面的语句。
- 执行一次循环后,更新循环控制变量。
- 再次检测布尔表达式。循环执行上面的过程。
for(初始化; 布尔表达式; 更新) { //代码语句 } -
foreach循环(增强 for 循环)
foreach的语句格式: for(元素类型t 元素变量x : 遍历对象obj){ 引用了x的java语句; }- 如果只是遍历集合或者数组,用foreach好些,快些。
- 如果对集合中的值进行修改,确定循环次数就要用for循环了。
- 关键字:
- break 关键字:中断循环。
- continue 关键字:作用是让程序立刻跳转到下一次循环的迭代。
-
if()…else if()…else:满足条件之执行if或if else()下的代码,不满足执行else下的代码。
-
switch case:
-
switch case 语句判断一个变量与一系列值中某个值是否相等,每个值称为一个分支。
-
语法格式
switch(expression){ case value : //语句 break; //可选 case value : //语句 break; //可选 //你可以有任意数量的case语句 default : //可选 //语句 }
-
(1)数组
-
数组的声明:
- 数据类型 [] 数组名称 = new 数据类型[长度];
int[] arr; //先声明 arr=new int[5]; //分配空间 for(int i=0;i<5;i++)arr[i]=i*10; //赋值int[] arr=new int[5]; //声明并分配空间 for(int i=0;i<5;i++)arr[i]=i*10; //赋值int[] arr={20,68,34,22,34}; //声明并分配空间然后.赋值 -
数组的遍历是采用循环遍历!
-
二维数组 :int【】【】
-
Array工具类的使用:CSDN大佬详细教程
-
排序算法(8种排序):冒泡排序、希尔排序、选择排序、插入排序、快速排序、归并排序、堆排序、基数排序。
-
缺点:长度固定,存在越界问题
-
注意:
- 数组下标(从0开始)超出数组长度,数组越界异常(运行时异常)
- 数组中每个元素都有默认值,默认值是该数据类型默认值
- 数组名称.length(属性):取得数组长度
- 数组可以被当作返回值返回!
(2)类(下面会细写)
(3)接口(下面会细写)
10、包机制- 域名倒写
- 防止命名冲突
- package:定义包(必须在第一行)
- import:导入包
- 常用的文档注释
| 标签 | 作用 |
|---|---|
| @author | 标志编写的作者 |
| @exception | 标志抛出的异常 |
| @param | 说明一个方法的参数 |
| @return | 说明方法的返回值类型 |
| @since | 说明该程序的JDK开始版本 |
| @throws | 和@exception标签一样 |
| @version | 说明版本 |
- cmd输入:javadoc -encoding UTF-8 -charset UTF-8 -d C:UsersadminDesktopJavadoc Circle.java
- idea中有自己的工具可以生成
-
什么方法:Java方法是语句的集合,它们在一起执行一个功能。
- 方法是解决一类问题的步骤的有序组合
- 方法包含于类或对象中
- 方法在程序中被创建,在其他地方被引用
-
方法的优点
- 使程序变得更简短而清晰。
- 有利于程序维护。
- 可以提高程序开发的效率。
- 提高了代码的重用性。
-
方法的命名规则
-
方法的名字的第一个单词应以小写字母作为开头,后面的单词则用大写字母开头写,不使用连接符。例如:addPerson。
-
方法的定义:
-
修饰符 返回值类型 方法名(参数类型 参数名){ ... 方法体 ... return 返回值; } 修饰符:修饰符,这是可选的,告诉编译器如何调用该方法。定义了该方法的访问类型。 返回值类型 :方法可能会返回值。returnValueType 是方法返回值的数据类型。有些方法执行所需的操作,但没有返回值。在这 种情况下,returnValueType 是关键字void。 方法名:是方法的实际名称。方法名和参数表共同构成方法签名。 参数类型:参数像是一个占位符。当方法被调用时,传递值给参数。这个值被称为实参或变量。参数列表是指方法的参数类型、顺序 和参数的个数。参数是可选的,方法可以不包含任何参数。 方法体:方法体包含具体的语句,定义该方法的功能。
-
-
方法的调用 :
- 类名.方法
- 对象.方法
-
概念:对象是类的一个实例(对象不是找个女朋友),有状态和行为。例如,一条狗是一个对象,它的属性有:颜色、名字、品种;行为有:摇尾巴、叫、吃等。
-
类是对象的抽象,抽象是类的具体。
-
面向对象的三大特性:封装、继承、多态!
-
Java作为一种面向对象语言。支持以下基本概念: 多态、继承、封装、抽象、类、对象、实例、方法、重载
- 继承: 子类继承父类的属性和方法
-
Java 中通过 extends 关键字可以申明一个类是从另外一个类继承而来的。
-
继承的特性:
子类拥有父类非 private 的属性、方法。
子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
子类可以用自己的方式实现父类的方法。
Java 的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如 B 类继承 A类,C 类继承 B 类,所以按照关系就是 B 类是 C 类的父类,A 类是 B 类的父类,这是 Java 继承区别于 C++ 继承的一个特性。
提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系越紧密,代码独立性越差)。
-
super 与 this 关键字
super关键字:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。
this关键字:指向自己的引用。 -
构造器:
**子类是不继承父类的构造器(构造方法或者构造函数)**的,它只是调用(隐式或显式)。如果父类的构造器带有参数,则必须在子类的构造器中显式地通过 super 关键字调用父类的构造器并配以适当的参数列表。
如果父类构造器没有参数,则在子类的构造器中不需要使用 super 关键字调用父类构造器,系统会自动调用父类的无参构造器。
-
- 方法发的重写(Override)与重载(Overload)
- 重写:重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!
- 重载:重载是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。
- 多态:
-
多态是同一个行为具有多个不同表现形式或形态的能力。
-
特点:消除类型之间的耦合关系、 可替换性、可扩充性、 接口性、灵活性、简化性
-
实现方式:
方式一:重写;
方式二:接口;
方式三:抽象类和抽象方法;
-
- 封装(该漏的漏,该藏得藏):
-
封装指一种将抽象性函式接口的实现细节部分包装、隐藏起来的方法。
-
属性私有get、set
-
步骤:
1、修改属性的可见性来限制对属性的访问(一般限制为private)
2、对每个值属性提供对外的公共方法访问,也就是创建一对赋取值方法,用于对私有属性的访问
-
- 继承: 子类继承父类的属性和方法
-
类是一个模板,它描述一类对象的行为和状态。
-
构造方法(一般用于对象的初始化):在创建一个对象的时候,至少要调用一个构造方法。构造方法的名称必须与类同名,一个类可以有多个构造方法。
-
new对象:栈存放引用,堆存放具体的对象。
-
创建对象三步
声明:声明一个对象,包括对象名称和对象类型。
实例化:使用关键字 new 来创建一个对象。
初始化:使用 new 创建对象时,会调用构造方法初始化对象。
-
-
构造方法
-
构造方法的重载
默认无参构造
如果手动定义了有参的构造方法就必须手动添加一个无参的构造方法。
-
instanceof 是 Java 的保留关键字。它的作用是测试它左边的对象是否是它右边的类的实例,返回 boolean 的数据类型。
-
-
内部类
- Number类:
- 在实际开发过程中,我们经常会遇到需要使用对象,而不是内置数据类型的情形。为了解决这个问题,Java 语言为每一个内置数据类型提供了对应的包装类。所有的包装类**(Integer、Long、Byte、Double、Float、Short)**都是抽象类 Number 的子类。
- Java 的 Math 包含了用于执行基本数学运算的属性和方法,如初等指数、对数、平方根和三角函数。
- Math 的方法都被定义为 static 形式,通过 Math 类可以在主函数中直接调用。
Number和Math 常用方法:
| 序号 | 方法 | 描述 |
|---|---|---|
| 1 | xxxValue() | 将 Number 对象转换为xxx数据类型的值并返回。 |
| 2 | compareTo() | 将number对象与参数比较。 |
| 3 | equals() | 判断number对象是否与参数相等。 |
| 4 | valueOf() | 返回一个 Number 对象指定的内置数据类型 |
| 5 | toString() | 以字符串形式返回值。 |
| 6 | parseInt() | 将字符串解析为int类型。 |
| 7 | abs() | 返回参数的绝对值。 |
| 8 | ceil() | 返回大于等于( >= )给定参数的的最小整数,类型为双精度浮点型。 |
| 9 | floor() | 返回小于等于(<=)给定参数的最大整数 。 |
| 10 | rint() | 返回与参数最接近的整数。返回类型为double。 |
| 11 | round() | 它表示四舍五入,算法为 Math.floor(x+0.5),即将原来的数字加上 0.5 后再向下取整,所以,Math.round(11.5) 的结果为12,Math.round(-11.5) 的结果为-11。 |
| 12 | min() | 返回两个参数中的最小值。 |
| 13 | max() | 返回两个参数中的最大值。 |
| 14 | exp() | 返回自然数底数e的参数次方。 |
| 15 | log() | 返回参数的自然数底数的对数值。 |
| 16 | pow() | 返回第一个参数的第二个参数次方。 |
| 17 | sqrt() | 求参数的算术平方根。 |
| 18 | sin() | 求指定double类型参数的正弦值。 |
| 19 | cos() | 求指定double类型参数的余弦值。 |
| 20 | tan() | 求指定double类型参数的正切值。 |
| 21 | asin() | 求指定double类型参数的反正弦值。 |
| 22 | acos() | 求指定double类型参数的反余弦值。 |
| 23 | atan() | 求指定double类型参数的反正切值。 |
| 24 | atan2() | 将笛卡尔坐标转换为极坐标,并返回极坐标的角度值。 |
| 25 | toDegrees() | 将参数转化为角度。 |
| 26 | toRadians() | 将角度转换为弧度。 |
| 27 | random() | 返回一个随机数。 |
- Object类是所有类的父类。一个类都会直接或者间接的继承自该类
- Object常的方法:
- toString()方法
- equals()方法 :比较两个对象是否相同,但是加了一些健壮性的判断!
- hashcode()方法:hashCode() 方法用于返回字符串的哈希码。
- getClass()方法:getClass()方法是获得调用该方法的对象的类;getClass().getName()可以得到该类的路径;
- notify() 方法:只能被作为此对象监视器的所有者的线程来调用。
- wait()方法:让当前线程进入等待状态。直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法。
- Character 类用于对单个字符进行操作。
- 一些常用的方法
| 序号 | 方法 | 描述 |
|---|---|---|
| 1 | isLetter() | 是否是一个字母 |
| 2 | isDigit() | 是否是一个数字字符 |
| 3 | isWhitespace() | 是否是一个空白字符 |
| 4 | isUpperCase() | 是否是大写字母 |
| 5 | isLowerCase() | 是否是小写字母 |
| 6 | toUpperCase() | 指定字母的大写形式 |
| 7 | toLowerCase() | 指定字母的小写形式 |
| 8 | toString() | 返回字符的字符串形式,字符串的长度仅为1 |
- 字符串广泛应用 在 Java 编程中,在 Java 中字符串属于**对象**,Java 提供了 String 类来创建和操作字符串。
- String 类是不可改变的,点击源码可以看到String是被final修饰的,所以你一旦创建了 String 对象,那它的值就无法改变了(详看笔记部分解析)。如果需要对字符串做很多修改,那么应该选择使用 StringBuffer
- 字符段长度:String 类的一个访问器方法是 length() 方法,它返回字符串对象包含的字符数。
- 连接字符串:
- string1.concat(string2);
- “Hello,” + " runoob" + “!”;
-
当对字符串进行修改的时候,需要使用 StringBuffer 。和 String 类不同的是,StringBuffer 的对象能够被多次的修改,并且不产生新的未使用对象。
-
主要方法
| 序号 | 方法描述 | 描述 |
|---|---|---|
| 1 | public StringBuffer append(String s) | 将指定的字符串追加到此字符序列。 |
| 2 | public StringBuffer reverse() | 将此字符序列用其反转形式取代。 |
| 3 | public delete(int start, int end) | 移除此序列的子字符串中的字符。 |
| 4 | public insert(int offset, int i) | 将 int 参数的字符串表示形式插入此序列中。 |
| 5 | insert(int offset, String str) | 将 str 参数的字符串插入此序列中。 |
| 6 | replace(int start, int end, String str) | 使用给定 String 中的字符替换此序列的子字符串中的字符。 |
-
java.util 包提供了 Date 类来封装当前的日期和时间。 Date 类提供两个构造函数来实例化 Date 对象。
-
第一个构造函数使用当前日期和时间来初始化对象:Date( )
-
第二个构造函数接收一个参数,该参数是从1970(源码时1900)年1月1日起的毫秒数:Date(long millisec)
-
常用方法:
| 序号 | 方法 | 描述 |
|---|---|---|
| 1 | boolean after(Date date) | 若当调用此方法的Date对象在指定日期之后返回true,否则返回false。 |
| 2 | boolean before(Date date) | 若当调用此方法的Date对象在指定日期之前返回true,否则返回false。 |
| 3 | int compareTo(Date date) | 比较当调用此方法的Date对象和指定日期。相等返回0。在之前则返回-1在之后返回1。 |
| 4 | boolean equals(Object date) | 当调用此方法的Date对象和指定日期相等时候返回true,否则返回false。 |
| 5 | String toString( ) | 把此 Date 对象转换为以下形式的 String: dow mon dd hh:mm:ss zzz yyyy 。 |
使用 SimpleDateFormat 格式化日期:
public static void main(String args[]) {
Date dNow = new Date( );
SimpleDateFormat ft = new SimpleDateFormat ("yyyy-MM-dd hh:mm:ss");
System.out.println("当前时间为: " + ft.format(dNow));
}
使用printf格式化日期:
public static void main(String args[]) {
// 初始化 Date 对象
Date date = new Date();
//c的使用
System.out.printf("全部日期和时间信息:%tc%n",date);
//f的使用
System.out.printf("年-月-日格式:%tF%n",date);
//d的使用
System.out.printf("月/日/年格式:%tD%n",date);
//r的使用
System.out.printf("HH:MM:SS PM格式(12时制):%tr%n",date);
//t的使用
System.out.printf("HH:MM:SS格式(24时制):%tT%n",date);
//R的使用
System.out.printf("HH:MM格式(24时制):%tR",date);
}
3、Calendar类:
-
我们现在已经能够格式化并创建一个日期对象了,但是我们如何才能设置和获取日期数据的特定部分呢,比如说小时,日,或者分钟? 我们又如何在日期的这些部分加上或者减去值呢? 答案是使用Calendar 类。
-
创建一个指定日期的对象:
Calendar c1 = Calendar.getInstance(); c1.set(2009, 6 - 1, 12);
| 常量 | 描述 |
|---|---|
| Calendar.YEAR | 年份 |
| Calendar.MONTH | 月份 |
| Calendar.DATE | 日期 |
| Calendar.DAY_OF_MONTH | 日期,和上面的字段意义完全相同 |
| Calendar.HOUR | 12小时制的小时 |
| Calendar.HOUR_OF_DAY | 24小时制的小时 |
| Calendar.MINUTE | 分钟 |
| Calendar.SECOND | 秒 |
| Calendar.DAY_OF_WEEK | 星期几 |
1、Scanner
-
Java.util.Scanner 是 Java5 的新特征,我们可以通过 Scanner 类来获取用户的输入。
-
Scanner的创建:
-
Scanner s = new Scanner(System.in);
-
-
通过 Scanner 类的 next() 与 nextLine() 方法获取输入的字符串,在读取前我们一般需要 使用 hasNext 与 hasNextLine 判断是否还有输入的数据:
2、 next() 与 nextLine() 区
next():
案例:
import java.util.Scanner;
public class ScannerDemo {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
// 从键盘接收数据
// next方式接收字符串
System.out.println("next方式接收:");
// 判断是否还有输入
if (scan.hasNext()) {
String str1 = scan.next();
System.out.println("输入的数据为:" + str1);
}
scan.close();
}
}
- 1、一定要读取到有效字符后才可以结束输入。
- 2、对输入有效字符之前遇到的空白,next() 方法会自动将其去掉。
- 3、只有输入有效字符后才将其后面输入的空白作为分隔符或者结束符。
- next() 不能得到带有空格的字符串。
nextLine():
案例:
import java.util.Scanner;
public class ScannerDemo {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
// 从键盘接收数据
// nextLine方式接收字符串
System.out.println("nextLine方式接收:");
// 判断是否还有输入
if (scan.hasNextLine()) {
String str2 = scan.nextLine();
System.out.println("输入的数据为:" + str2);
}
scan.close();
}
}
- 1、以Enter为结束符,也就是说 nextLine()方法返回的是输入回车之前的所有字符。
- 2、可以获得空白。
-
介绍:
Java在java.math包中提供的API类BigDecimal,用来对超过16位有效位的数进行精确的运算。双精度浮点型变量double可以处理16位有效数。在实际应用中,需要对更大或者更小的数进行运算和处理。float和double只能用来做科学计算或者是工程计算,在商业计算中要用java.math.BigDecimal。BigDecimal所创建的是对象,我们不能使用传统的+、-、*、/等算术运算符直接对其对象进行数学运算,而必须调用其相对应的方法。方法中的参数也必须是BigDecimal的对象。构造器是类的特殊方法,专门用来创建对象,特别是带有参数的对象。
-
BigDecimal一共有4个构造方法:
# BigDecimal(int) 创建一个具有参数所指定整数值的对象。 BigDecimal(double) 创建一个具有参数所指定双精度值的对象。(不建议采用) BigDecimal(long) 创建一个具有参数所指定长整数值的对象。 BigDecimal(String) 创建一个具有参数所指定以字符串表示的数值的对象 [1] 。
- 在Java编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。
- 接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。
- 除非实现接口的类是抽象类,否则该类要定义接口中的所有方法。
- 接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。另外,在 Java 中,接口类型可用来声明一个变量,他们可以成为一个空指针,或是被绑定在一个以此接口实现的对象。
- 一个接口可以有多个方法。
- 接口文件保存在 .java 结尾的文件中,文件名使用接口名。
- 接口的字节码文件保存在 .class 结尾的文件中。
- 接口相应的字节码文件必须在与包名称相匹配的目录结构中。
- 接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)。
- 接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量(并且只能是 public,用 private 修饰会报编译错误)。
- 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法。
-
[可见度] interface 接口名称 [extends 其他的接口名] { // 声明变量 // 抽象方法 }- 接口是隐式抽象的,当声明一个接口的时候,不必使用abstract关键字。
- 接口中每一个方法也是隐式抽象的,声明时同样不需要abstract关键字。
- 接口中的方法都是公有的。
-
...implements 接口名称[, 其他接口名称, 其他接口名称..., ...] ...
- 一个类可以同时实现多个接口。
- 一个类只能继承一个类,但是能实现多个接口。
- 一个接口能继承另一个接口,这和类之间的继承比较相似。
-
接口的继承:接口的继承使用extends关键字,子接口继承父接口的方法。
+++
三、Java 流(Stream)、文件(File)和IO 1、Java IO流的概念:- Java.io 包几乎包含了所有操作输入、输出需要的类。所有这些流类代表了输入源和输出目标。
- Java.io 包中的流支持很多种格式,比如:基本类型、对象、本地化字符集等等。
- 一个流可以理解为一个数据的序列。输入流表示从一个源读取数据,输出流表示向一个目标写数据。
- **注:**java的输入流主要是InputStream和Reader作为基类,而输出流则是主要由outputStream和Writer作为基类。它们都是一些抽象基类,无法直接创建实例。
- 字节流和字符流的用法几乎完成全一样,区别在于字节流和字符流所操作的数据单元不同,字节流操作的单元是数据单元是8位的字节,字符流操作的是数据单元为16位的字符。
- 字节流主要是由InputStream和outPutStream作为基类,而字符流则主要有Reader和Writer作为基类。
(2)按照流的角色划分为节点流和处理流。
- 当使用节点流进行输入和输出时,程序直接连接到实际的数据源,和实际的输入/输出节点连接。
- 当使用处理流进行输入/输出时,程序并不会直接连接到实际的数据源,没有和实际的输入和输出节点连接。使用处理流的一个明显的好处是,只要使用相同的处理流,程序就可以采用完全相同的输入/输出代码来访问不同的数据源,随着处理流所包装的节点流的变化,程序实际所访问的数据源也相应的发生变化。
++++
四、异常 1、异常分类:- **检查性异常:**最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。
- 运行时异常: 运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。
- 错误: 错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,它们在编译也检查不到的。
- Exception:运行时异常(1/0,ClasssNotFound,NullPoint,UnKnowType,下标越界)
- Error:AWT错误,JVM错误(StackOverFlow,OutOfMemory)
- Java 内置异常类
(1)使用 try 和 catch 关键字可以捕获异常。
try
{
// 程序代码
}catch(ExceptionName e1)
{
//Catch 块
}finally{
//必定会执行的部分
}
(2)throw:手动抛出异常。
(3)throws:方法自动抛出异常
(4)声明自定义异常
在 Java 中你可以自定义异常。编写自己的异常类时需要记住下面的几点。
- 所有异常都必须是 Throwable 的子类。
- 如果希望写一个检查性异常类,则需要继承 Exception 类。
- 如果你想写一个运行时异常类,那么需要继承 RuntimeException 类。
(1)集合的由来
- Java是面向对象的语言,我们在编程的时候自然需要存储对象的容器,数组可以满足这个需求,但是数组初始化时长度是固定的,但是我们往往需要一个长度可变化的容器,因此,集合出现了。
(2)集合和数组的区别
-
长度区别:集合长度可变,数组长度不可变
-
内容区别:集合可存储不同类型元素,数组存储只可单一类型元素
-
元素区别:集合只能存储引用类型元素,数组可存储引用类型,也可存储基本类型
(3)Java集合框架图:
- Java集合要从两大接口说起,一为Collection接口,二为Map接口,它们是同一个层次的。
Collection接口被List接口和Set接口继承;
List接口有三个实现类,ArrayList,linkedList,Vector;
Set接口被HashSet类实现,被SortedSet接口继承,同时TreeSet类实现SortedSet接口,linkedHashSet类继承HashSet类;
Map接口有两个实现类,HashMap,HashTable,同时Propertise类继承HashTable;
2、Collection接口(单列集合) 2.1 List接口: (1)是什么?Map接口被SortedMap接口继承,同时TreeMap类实现了SortedMap接口;
- List是一个有序的集合,List主要分为3类,ArrayList, linkedList和Vector。
- 特点:
- 有索引,精准操作元素;
- 元素有序,存储及取出时顺序一致;
- 元素可重复,通过.equals()比较是否重复。
- get(int index,E e) 获取指定位置的元素;remove(int index)移除指定位置的元素; add(int index,E e) 将元素添加到指定位置;set(int index,E e) 用元素替换指定位置的元素;
- 数据结构:数组;
- 特点:查询快,增删慢,主要用于查询遍历数据,为最常用集合之一;
- 底层分析:数组结构是有序的元素序列,在内存中开辟一段连续的空间,在空间中存放元素,每个空间都有编号,通过编号可以快速找到相应元素,因此查询快;数组初始化时长度是固定的,要想增删元素,必须创建一个新数组,把源数组的元素复制进来,随后源数组销毁,耗时长,因此增删慢。
- 数据结构:双向链表;
- 特点:查询慢,增删快;
- 底层分析:链表分为单向和双向,就是一条链子和两条链子的区别;多出的那条链子记录了元素的顺序,因此单向链表结构无序,双向链表结构有序;链表结构没有索引,因此查询慢;链表的增删只需在原有的基础上连上链子或切断链子,因此增删快。
- 特有方法:
- getFirst() 返回开头元素; getLast() 返回结尾元素;
- pop() 从所在堆栈中获取一个元素; push(E e) 将元素推入所在堆栈;
- addFirst(E e) 添加元素到开头,头插; addLast(E e) 添加元素到结尾,尾插;
-
数据结构:数组;
-
特点:查询快,增删慢
-
底层分析:和ArrayList一样,都是数组实现,因此具有相似的特性,它们之间的区别在于Vector是线程安全的,效率低,ArrayList是线程不安全的,但效率高。
-
Set和List一样,也继承于Collection,是集合的一种,和List不同的是,Set内部实现是基于Map的,所以Set取值时不保证数据和存入的时候顺序一致,并且不允许空值,不允许重复值。
-
特点:
- 元素不可重复;
- 元素无序,存储及取出时顺序不一致;
- 没有索引,因此不能使用普通For循环遍历;
- Set与Collection 接口中的方法基本一致,没有进行功能上的扩充;
-
Set主要有2个实现方式,一个是TreeSet,另一个是HashSet
-
数据结构:JDK1.8之前:哈希表(数组+单向链表);JDK1.8之后:哈希表(数组+单向链表+红黑树),当链表长度超过阈值(8)时,链表将转换为红黑树。
-
特点:查询快,元素无序,元素不可重复,没有索引;
-
底层分析:哈希表底层用数组+单向链表实现,即使用链表处理冲突,同一Hash值的元素都存储在一个链表里,但是当位于一个链表中的元素较多,即Hash值相等的元素较多,通过key值依次查找的效率降低。JDK1.8之后,哈希表底层采用数据+单向链表+红黑树实现,当链表长度超过阈值(8)时,链表将转换为红黑树,极大缩短查询时间。
-
ps:哈希值是一个十进制的整数,是对象的地址值,是一个逻辑地址,不是实际存储的物理地址,由系统随机给出。Object类的int hashCode()方法,可以获取对象的哈希值。
-
数据结构:红黑树
-
特点:查询快,元素有序,元素不可重复,没有索引;
-
底层分析:TreeSet实现了继承于Set接口的SortedSet接口 ,它支持两种排序方法,自然排序和定制排序,自然排序的意思就是放入元素“a”,“b”,a会自然地排在b前面,其中还有几个特有方法。
-
first() 返回第一个元素; last() 返回最后一个元素;comparator() 返回排序比较器;
- Set 接口实例存储的是无序的,不重复的数据。List 接口实例存储的是有序的,可以重复的元素。
- Set检索效率低下,删除和插入效率高,插入和删除不会引起元素位置改变 <实现类有HashSet,TreeSet>。
- List和数组类似,可以动态增长,根据实际存储的数据的长度自动增长List的长度。查找元素效率高,插入删除效率低,因为会引起其他元素位置改变 <实现类有ArrayList,linkedList,Vector> 。
+++
3、Map接口(双列集合) 3.1 介绍:- 特点:元素包含两个值(key,value)即键值对, key不允许重复,value可以重复, key与value是一一对应的。元素无序;
- Map接口是双列集合的最顶层接口,定义了一些通用的方法。
- put(key , value) 添加元素; remove(key) 删除key对应元素;containsKey(key) 判断是否存在key对应元素;get(key) 获取key对应元素;KeySet() 获取所有的key,存到Set集合中;entrySet() 获取所有的元素,存到Set集合中;
- ps:Map集合必须保证保证key唯一,作为key,必须重写hashCode方法和equals方法,以保证key唯一。
-
数据结构:JDK1.8之前:哈希表(数组+单向链表);JDK1.8之后:哈希表(数组+单向链表+红黑树),当链表长度超过阈值(8)时,链表将转换为红黑树。
-
特点:查询快,元素无序,key不允许重复但可以为null,value可以重复。
-
底层分析:和HashSet底层相类似。
-
具体使用:菜鸟HashMap
import java.util.*;
public class Test{
public static void main(String[] args) {
List list=new ArrayList();
list.add("Hello");
list.add("World");
list.add("HAHAHAHA");
//第一种遍历方法使用 For-Each 遍历 List
for (String str : list) { //也可以改写 for(int i=0;i ite=list.iterator();
while(ite.hasNext())//判断下一个元素之后有值
{
System.out.println(ite.next());
}
}
}
4.2 遍历 Map
import java.util.*;
public class Test{
public static void main(String[] args) {
Map map = new HashMap();
map.put("1", "value1");
map.put("2", "value2");
map.put("3", "value3");
//第一种:普遍使用,二次取值
System.out.println("通过Map.keySet遍历key和value:");
for (String key : map.keySet()) {
System.out.println("key= "+ key + " and value= " + map.get(key));
}
//第二种
System.out.println("通过Map.entrySet使用iterator遍历key和value:");
Iterator> it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = it.next();
System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
}
//第三种:推荐,尤其是容量大时
System.out.println("通过Map.entrySet遍历key和value");
for (Map.Entry entry : map.entrySet()) {
System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
}
//第四种
System.out.println("通过Map.values()遍历所有的value,但不能遍历key");
for (String v : map.values()) {
System.out.println("value= " + v);
}
}
}
5、迭代器(Iterator)
(1)简介:
-
迭代器(Iterator)是一个对象,它的工作就是遍历并选择序列中的对象,它提供了一种访问容器(container)对象中的各个元素,而又不必暴露该对象内部细节的方法。
-
迭代器通常被称为“轻量级”对象,因为创建它的代价小。
-
迭代器 it 的两个基本操作是 next 、hasNext 和 remove。
调用 it.next() 会返回迭代器的下一个元素,并且更新迭代器的状态。
调用 it.hasNext() 用于检测集合中是否还有元素。
调用 it.remove() 将迭代器返回的元素删除。
(2)使用:
-
导入包: import java.util.Iterator; // 引入 Iterator 类
-
让迭代器 it 逐个返回集合中所有元素最简单的方法是使用 while 循环:
while(it.hasNext()) { System.out.println(it.next()); } -
删除元素:要删除集合中的元素可以使用 remove() 方法。
具体使用:CSDN上的大佬文章
菜鸟教程
六、多线程 1、线程和进程 1.1 什么是进程,什么是线程:- 进程:一个在内存中运行的应用程序。每个进程都有自己独立的一块内存空间,一个进程可以有多个线程,比如在Windows系统中,一个运行的xx.exe就是一个进程。
- 线程:进程中的一个执行任务(控制单元),负责当前进程中程序的执行。一个进程至少有一个线程,一个进程可以运行多个线程,多个线程可共享数据。
-
Java程序的进程里有几个线程:主线程,垃圾回收线程(后台线程)等
-
在 Java 中,当我们启动 main 函数时其实就是启动了一个 JVM 的进程,而 main 函数所在的线程就是这个进程中的一个线程,也称主线程。
-
Java支持多线程,当Java程序执行main方法的时候,就是在执行一个名字叫做main的线程,可以在main方法执行时,开启多个线程A,B,C,多个线程 main,A,B,C同时执行,相互抢夺CPU,Thread类是java.lang包下的一个常用类,每一个Thread类的对象,就代表一个处于某种状态的线程
-
新建状态:
使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。
-
就绪状态:
当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。
-
运行状态:
如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。
-
阻塞状态:
如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:
- 等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。
- 同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。
- 其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。
-
死亡状态:
一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。
每一个 Java 线程都有一个优先级,这样有助于操作系统确定线程的调度顺序。
Java 线程的优先级是一个整数,其取值范围是 1 (Thread.MIN_PRIORITY ) - 10 (Thread.MAX_PRIORITY )。
默认情况下,每一个线程都会分配一个优先级 NORM_PRIORITY(5)。
具有较高优先级的线程对程序更重要,并且应该在低优先级的线程之前分配处理器资源。但是,线程优先级不能保证线程执行的顺序,而且非常依赖于平台。
2.3 线程的创建Java 提供了三种创建线程的方法:
- 通过实现 Runnable 接口;
- 通过继承 Thread 类本身;
- 通过 Callable 和 Future 创建线程。
- 创建一个线程,最简单的方法是创建一个实现 Runnable 接口的类。为了实现 Runnable,一个类只需要执行一个方法调用 run(),你可以重写该方法,重要的是理解的 run() 可以调用其他方法,使用其他类,并声明变量,就像主线程一样。
- 新线程创建之后,你调用它的 start() 方法它才会运行。
class RunnableDemo implements Runnable {
private Thread t;
private String threadName;
RunnableDemo( String name) {
threadName = name;
System.out.println("Creating " + threadName );
}
public void run() {
System.out.println("Running " + threadName );
try {
for(int i = 4; i > 0; i--) {
System.out.println("Thread: " + threadName + ", " + i);
// 让线程睡眠一会
Thread.sleep(50);
}
}catch (InterruptedException e) {
System.out.println("Thread " + threadName + " interrupted.");
}
System.out.println("Thread " + threadName + " exiting.");
}
public void start () {
System.out.println("Starting " + threadName );
if (t == null) {
t = new Thread (this, threadName);
t.start ();
}
}
}
public class TestThread {
public static void main(String args[]) {
RunnableDemo R1 = new RunnableDemo( "Thread-1");
R1.start();
RunnableDemo R2 = new RunnableDemo( "Thread-2");
R2.start();
}
}
(2) 通过继承Thread来创建线程
- 创建一个线程的第二种方法是创建一个新的类,该类继承 Thread 类,然后创建一个该类的实例。
- 继承类必须重写 run() 方法,该方法是新线程的入口点。它也必须调用 start() 方法才能执行。
- 该方法尽管被列为一种多线程实现方式,但是本质上也是实现了 Runnable 接口的一个实例。
class ThreadDemo extends Thread {
private Thread t;
private String threadName;
ThreadDemo( String name) {
threadName = name;
System.out.println("Creating " + threadName );
}
public void run() {
System.out.println("Running " + threadName );
try {
for(int i = 4; i > 0; i--) {
System.out.println("Thread: " + threadName + ", " + i);
// 让线程睡眠一会
Thread.sleep(50);
}
}catch (InterruptedException e) {
System.out.println("Thread " + threadName + " interrupted.");
}
System.out.println("Thread " + threadName + " exiting.");
}
public void start () {
System.out.println("Starting " + threadName );
if (t == null) {
t = new Thread (this, threadName);
t.start ();
}
}
}
public class TestThread {
public static void main(String args[]) {
ThreadDemo T1 = new ThreadDemo( "Thread-1");
T1.start();
ThreadDemo T2 = new ThreadDemo( "Thread-2");
T2.start();
}
}
(3)通过 Callable 和 Future 创建线程
- 创建 Callable 接口的实现类,并实现 call() 方法,该 call() 方法将作为线程执行体,并且有返回值。
- 创建 Callable 实现类的实例,使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的 call() 方法的返回值。
- 使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程。
- 调用 FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值。
- 采用实现 Runnable、Callable 接口的方式创建多线程时,线程类只是实现了 Runnable 接口或 Callable 接口,还可以继承其他类。
- 使用继承 Thread 类的方式创建多线程时,编写简单,如果需要访问当前线程,则无需使用 Thread.currentThread() 方法,直接使用 this 即可获得当前线程。



