文章目录
java基础
基础知识
关键字
类别 | 关键字 | 说明 |
---|---|---|
访问控制 | private | 私有的 |
protected | protected | |
public | 公共的 | |
default | 默认 | |
类、方法和变量修饰符 | abstract | 声明抽象 |
class | 类 | |
extends | 扩充,继承 | |
final | 最终值,不可改变的 | |
implements | 继承、实现(接口) | |
interface | 接口 | |
native | 本地,原生方法(非 Java 实现) | |
new | 新,(创建对象) | |
static | 静态 | |
strictfp | 严格,精准 | |
synchronized | 线程,同步 | |
transient | 短暂 | |
volatile | 易失 | |
程序控制语句 | break | 跳出循环 |
case | 定义一个值以供 switch 选择 | |
continue | 继续 | |
do | 运行 | |
else | 否则 | |
for | 循环 | |
if | 如果 | |
instanceof | 实例 | |
return | 返回 | |
switch | 根据值选择执行 | |
while | 循环 | |
错误处理 | assert | 断言表达式是否为真 |
catch | 捕捉异常 | |
finally | 有没有异常都执行 | |
throw | 抛出一个异常对象 | |
throws | 声明一个异常可能被抛出 | |
try | 捕获异常 | |
包相关 | import | 引入 |
package | 包 | |
基本类型 | boolean | 布尔型 |
byte | 字节型 | |
char | 字符型 | |
double | 双精度浮点 | |
float | 单精度浮点 | |
int | 整型 | |
long | 长整型 | |
short | 短整型 | |
变量引用 | super | 父类,超类 |
this | 本类 | |
void | 无返回值 | |
保留关键字 | goto | 是关键字,但不能使用 |
const | 是关键字,但不能使用 |
访问权限
修饰符 | 当前类 | 同一包内 | 子孙类(同一包) | 子孙类(不同包) | 其他包 |
---|---|---|---|---|---|
public | Y | Y | Y | Y | Y |
protected | Y | Y | Y | Y/N(说明) | N |
default | Y | Y | Y | N | N |
private | Y | N | N | N | N |
注意:Java 的 null 不是关键字,类似于 true 和 false,它是一个字面常量,不允许作为标识符使用。
修饰符:
- default (即默认,什么也不写): 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。
- private : 在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类)
- public : 对所有类可见。使用对象:类、接口、变量、方法
- protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类)
基本语法
编写 Java 程序时,应注意以下几点:
- 大小写敏感:Java 是大小写敏感的,这就意味着标识符 Hello 与 hello 是不同的。
- 类名:对于所有的类来说,类名的首字母应该大写。如果类名由若干单词组成,那么每个单词的首字母应该大写,例如 MyFirstJavaClass 。
- 方法名:所有的方法名都应该以小写字母开头。如果方法名含有若干单词,则后面的每个单词首字母大写。
- 源文件名:==源文件名必须和类名相同。==当保存文件的时候,你应该使用类名作为文件名保存(切记 Java 是大小写敏感的),文件名的后缀为 .java。(如果文件名和类名不相同则会导致编译错误)。
- 主方法入口:所有的 Java 程序由 public static void main(String[] args) 方法开始执行
基本数据类型
基本类型 | 字节(byte) | 位数 | 默认值 |
---|---|---|---|
int | 4 | 32 | 0 |
short | 2 | 16 | 0 |
long | 8 | 64 | 0L |
float | 4 | 32 | 0.0f |
double | 8 | 64 | 0.0d |
byte | 1 | 8 | 0 |
boolean | false | ||
char | 2 | 16 | ‘u0000’ |
String | null |
包装类 | 基本数据类型 |
---|---|
Boolean | boolean |
Byte | byte |
Short | short |
Integer | int |
Long | long |
Character | char |
Float | float |
Double | double |
引用类型
- 在Java中,引用类型的变量非常类似于C/C++的指针。引用类型指向一个对象,指向对象的变量是引用变量。这些变量在声明时被指定为一个特定的类型,比如 Employee、Puppy 等。变量一旦声明后,类型就不能被改变了。
- 对象、数组都是引用数据类型。
- 所有引用类型的默认值都是null。
类型转换
低转高–自动转换
高转低–强制类型转换
整型、实型(常量)、字符型数据可以混合运算。运算中,不同类型的数据先转化为同一类型,然后进行运算。
规则:
- 不能对boolean类型进行类型转换。
- 不能把对象类型转换成不相关类的对象。
- 在把容量大的类型转换为容量小的类型时必须使用强制类型转换。
- 转换过程中可能导致溢出或损失精度
低 ------------------------------------> 高
byte,short,char—> int —> long—> float —> double
自动类型转换
public class ZiDongLeiZhuan{
public static void main(String[] args){
char c1='a';//定义一个char类型
int i1 = c1;//char自动类型转换为int
System.out.println("char自动类型转换为int后的值等于"+i1);
char c2 = 'A';//定义一个char类型
int i2 = c2+1;//char 类型和 int 类型计算
System.out.println("char类型和int计算后的值等于"+i2);
}
}
强制类型转换
int i1 = 123;
byte b = (byte)i1;//(要转换的类型),用b存储转换后的值
System.out.println("int强制类型转换为byte后的值等于"+b);
装箱和拆箱
装箱其实就是调用了包装类的valueOf()方法,拆箱其实就是调用了 xxxValue()方法。
装箱:将基本类型用它们对应的引用类型包装起来;
拆箱:将包装类型转换为基本数据类型;
Integer i = 10; //装箱int n = i; //拆箱
因此,
- Integer i = 10 等价于 Integer i = Integer.valueOf(10)
- int n = i 等价于 int n = i.intValue();
注意:如果频繁拆装箱的话,也会严重影响系统的性能。我们应该尽量避免不必要的拆装箱操作。
变量
- **局部变量(Local Variables):**定义在方法、构造方法或语句块中的变量,作用域只限于当前方法、构造方法或语句块中。局部变量必须在使用前声明,并且不能被访问修饰符修饰。
- **成员变量(Instance Variables):**定义在类中、方法之外的变量,作用域为整个类,可以被类中的任何方法、构造方法和语句块访问。成员变量可以被访问修饰符修饰。
- **静态变量(Class Variables):**定义在类中、方法之外的变量,并且使用
static
关键字修饰,作用域为整个类,可以被类中的任何方法、构造方法和语句块访问,静态变量的值在程序运行期间只有一个副本。静态变量可以被访问修饰符修饰。 - **参数变量(Parameters):**方法定义时声明的变量,作为调用该方法时传递给方法的值。参数变量的作用域只限于方法内部
值传递和引用传递
- 值传递:在方法调用时,传递的是实际参数的值的副本。当参数变量被赋予新的值时,只会修改副本的值,不会影响原始值。Java 中的基本数据类型都采用值传递方式传递参数变量的值。
- 引用传递:在方法调用时,传递的是实际参数的引用(即内存地址)。当参数变量被赋予新的值时,会修改原始值的内容。Java 中的对象类型采用引用传递方式传递参数变量的值。
public class RunoobTest {
// 成员变量
private int instanceVar;
// 静态变量
private static int staticVar;
public void method(int paramVar) {
// 局部变量
int localVar = 10;
// 使用变量
instanceVar = localVar;
staticVar = paramVar;
System.out.println("成员变量: " + instanceVar);
System.out.println("静态变量: " + staticVar);
System.out.println("参数变量: " + paramVar);
System.out.println("局部变量: " + localVar);
}
public static void main(String[] args) {
Variables v = new Variables();
v.method(20);
}
}
运算符
条件运算符(?:)
条件运算符也被称为三元运算符。该运算符有3个操作数,并且需要判断布尔表达式的值。该运算符的主要是决定哪个值应该赋值给变量。
variable x = (expression) ? value if true : value if false
instanceof 运算符(判断接口)
该运算符用于操作对象实例,检查该对象是否是一个特定类型(类类型或接口类型)。
instanceof运算符使用格式如下:
String name = "James";
boolean result = name instanceof String; // 由于 name 是 String 类型,所以返回真
输入(Scanner)
Java的格式化功能提供了多种占位符,可以把各种数据类型“格式化”成指定的字符串:
占位符 | 说明 |
---|---|
%d | 格式化输出整数 |
%x | 格式化输出十六进制整数 |
%f | 格式化输出浮点数 |
%e | 格式化输出科学计数法表示的浮点数 |
%s | 格式化字符串 |
1.创建对象:Scanner sc = new Scanner(System.in);
String input = sc.next();
2.常用的next()方法系列:
- nextInt():输入整数,
- nextLine():输入字符串,
- nextDouble():输入双精度数,
- next():输入字符串(以空格作为分隔符)。
转义字符
转义序列 | 描述 |
---|---|
\t | 在文中该处插入一个tab键 |
\b | 在文中该处插入一个后退键 |
\n | 在文中该处换行 |
\r | 在文中该处插入回车 |
\f | 在文中该处插入换页符 |
\’ | 在文中该处插入单引号 |
\" | 在文中该处插入双引号 |
\ | 在文中该处插入反斜杠 |
源文件声明规则
- 一个源文件中只能有一个 public 类
- 一个源文件可以有多个非 public 类
- 源文件的名称应该和 public 类的类名保持一致。例如:源文件中 public 类的类名是 Employee,那么源文件应该命名为Employee.java。
- 如果一个类定义在某个包中,那么 package 语句应该在源文件的首行。
- 如果源文件包含 import 语句,那么应该放在 package 语句和类定义之间。如果没有 package 语句,那么 import 语句应该在源文件中最前面。
- import 语句和 package 语句对源文件中定义的所有类都有效。在同一源文件中,不能给不同的类不同的包声明。
IO流
- 输入流就是读数据
- 输出流就是写数据
1.java File类
作用:获取文件的信息和在磁盘创建新的文件等,这就需要学习使用File类。需要注意的是,不涉及对文件的读写操作。
创建一个File对象的构造方法有三个:
1)File(String filename);
2)File(String directoryPath,String filename);
3)File(File dir,String filename);
其中,filename是文件名字,directoryPath是文件的路径,dir为一个目录。
注:只有执行了createNewFile()才能创建成功
package Text;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.IOException;
public class IO {
public static void main(String[] args) {
}
//方式1呢哇File(String pathname)
@Test
public void creat01(){
String filePath = "d:\\new01.txt";
File file = new File(filePath);
try {
file.createNewFile();
System.out.println("创建成功");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
//方式2 new File(File parent,String child)
//根据父目录文件+子路径创建
@Test
public void creart02(){
File parentFile = new File("d:\\");
String fileName = "new2.txt";
File file = new File(parentFile,fileName);
//只有执行了createNewFile才能创建成功
try {
file.createNewFile();
System.out.println("创建成功");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
//方式3 new File(File parent,String child)
@Test
public void creat03(){
String parent = "d:\\";
String fileName = "new03";
File file = new File(parent,fileName);
try {
file.createNewFile();
System.out.println("创建成功");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
2.获取文件信息
package Text;
import org.junit.jupiter.api.Test;
import java.io.File;
//获取文件信息
public class IOInformation {
public static void main(String[] args) {
}
@Test
public void iofo(){
File file = new File("d:\\new01.txt");
//调用对应的方法,获得对应的信息
System.out.println(file.getName());//文件名字
System.out.println(file.length());//文件大小(字节)
System.out.println(file.exists());//判断文件是否存在
System.out.println(file.getParent());//父级目录
System.out.println(file.getPath());//路径
System.out.println(file.getAbsoluteFile());//绝对路径
System.out.println(file.isFile());//判断是否为文件
System.out.println(file.isDirectory());//判断是否目录
}
}
3.目录操作
- mkdir创建一级目录,mkdirs创建多级目录
package Text;
import org.junit.jupiter.api.Test;
import java.io.File;
public class Dirctory {
public static void main(String[] args) {
}
@Test
//判断文件是否存在,存在就删除
//目录也是被当作文件来对待
public void m1(){
String filePath = "d:\\new1.txt";
File file1 = new File(filePath);
if(file1.exists()){
if(file1.delete())
{
System.out.println("文件删除成功");
}else
{
System.out.println("······");
}
}
else{
System.out.println("文件不存在");
}
}
@Test
//判断目录"d:\demo"是否存在,存在就删除
public void m2(){
String filePath = "d:\\demo";
File file2 = new File(filePath);
if(file2.exists()){
if(file2.delete())
{
System.out.println("目录删除成功");
}else
{
System.out.println("······");
}
}
else{
System.out.println("目录不存在");
}
}
@Test
//判断目录"d:\\a\\b\\c"是否存在,不存在就创建
public void m3(){
String directory= "d:\\a\\b\\c";
File file3 = new File(directory);
if(file3.exists()){
System.out.println(directory+"存在");
}
else{
if(file3.mkdirs()){//mkdir创建一级目录,mkdirs创建多级目录
System.out.println("创建成功");
}
else{
System.out.println("创建失败");
}
}
}
}
4.IO原理和分类
抽象基类 | 字节流(8bit) | 字符流(多用于文本文件) |
---|---|---|
输入流 | InputStream | Reader(读入) |
输出流 | OutputStream | Writer(写出) |
-
IO涉及的流有40多个
-
父类的名字作为子类名字的后缀
5.FileInputStream
FileInputStream
:文件输入流BufferInputStream
:缓存输入流
- 该流用于从文件读取数据,它的对象可以用关键字 new 来创建。
- 有多种构造方法可用来创建对象。
- 可以使用字符串类型的文件名来创建一个输入流对象来读取文件:
InputStream f = new FileInputStream("C:/java/hello");
- 也可以使用一个文件对象来创建一个输入流对象来读取文件。我们首先得使用 File() 方法来创建一个文件对象:
File f = new File("C:/java/hello"); InputStream in = new FileInputStream(f);
package Text;
import org.junit.jupiter.api.Test;
import java.io.FileInputStream;
import java.io.IOException;
public class InpurStreamTest {
public static void main(String[] args) {
}
//使用InputStream读取文件内容(单个字节的读取)
@Test
public void readfile1() {
String Path = "d:\\new.txt";
int ReadData = 0;
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream(Path);
while ((ReadData = fileInputStream.read()) != -1)//1-表示读取完成
{
System.out.print((char) ReadData);
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
fileInputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
@Test
//使用byte[]来读取,可以读取多字符
public void readfile2() {
String Path = "d:\\new.txt";
int ReadData = 0;
byte[] buf = new byte[8];//一次读取8个字节
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream(Path);
while ((ReadData = fileInputStream.read(buf)) != -1)//1-表示读取完成
{
System.out.print(new String(buf, 0, ReadData));
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
fileInputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
6.FileOutputStream
- 类用来创建一个文件并向文件中写数据。
- 如果该流在打开文件进行输出前,目标文件不存在,那么该流会创建该文件。
- 有两个构造方法可以用来创建
FileOutputStream
对象。 - 使用字符串类型的文件名来创建一个输出流对象:
OutputStream f = new FileOutputStream("C:/java/hello")
- 也可以使用一个文件对象来创建一个输出流来写文件。我们首先得使用File()方法来创建一个文件对象:
File f = new File("C:/java/hello"); OutputStream fOut = new FileOutputStream(f);
package Text;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class OutputStreamTest {
public static void main(String[] args) {
}
@Test
public void Output1(){
String filePath = "d:\\new.txt";
FileOutputStream fileoutputStream1 = null;
FileOutputStream fileOutputStream2 = null;
try {
//有两种不同的创建方式
//1.new FileOutputStream(filePath)创建方式,会覆盖内容
//2.new FileOutputStream(filePath,true)创建方式,会追加在文件后面
fileoutputStream1 = new FileOutputStream(filePath);
fileOutputStream2 = new FileOutputStream(filePath,true);
//写入方式
//1.字节写入方式,fileoutputStream1.write('a');
//2.字符写入方式,str.getBytes()
fileoutputStream1.write('a');//写入一个字节
String str = "niubi";
fileoutputStream1.write(str.getBytes());//写入字符
// str.getBytes[]可以把字符->字节数组
fileoutputStream1.write(str.getBytes(),0,str.length());//0~length
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
try {
fileoutputStream1.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
7.FileReader
-
该类按字符向流中写入数据
-
文件字符输入流,读取文件信息
package IO;
import org.junit.jupiter.api.Test;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class FileReaderTest {
public static void main(String[] args) {
}
@Test
/**法一
* 使用FileReader流(文件输出流)来读取文件信息
* 单个字符的读取
*/
public void r1() {
String filePath = "d:\\new.txt";
FileReader fileReader = null;
int data = 0;
try {
fileReader = new FileReader(filePath);
while ((data = fileReader.read()) != -1) {
System.out.print((char) data);
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
fileReader.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
@Test
/**法二
* 使用FileReader流(文件输入流)来读取文件信息
* 使用字符数组来读取
*/
public void r2() {
String filePath = "d:\\new.txt";
FileReader fileReader = null;
char[] buf = new char[8];//字符数组,一次读取8个字节
int data = 0;
try {
fileReader = new FileReader(filePath);
while ((data = fileReader.read(buf)) != -1) {
System.out.print(new String(buf,0,data));//new String转为字符串输出
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
fileReader.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
8.FileWriter
-
该类按字符向流中写入数据
-
写信息进文件
-
一定要关闭流或者刷新
-
5种写入方法
package IO;
import org.junit.jupiter.api.Test;
import java.io.FileWriter;
import java.io.IOException;
public class FileWriterTest {
public static void main(String[] args) {
}
@Test
public void w1(){
String filePath = "d:\\new.txt";
FileWriter fileWriter = null;
char[] chars = {'a','b','c'};
try {
fileWriter = new FileWriter(filePath);//默认是覆盖写入
fileWriter.write('H');//单个字符的写入
fileWriter.write(chars);//写入一个数组
fileWriter.write(chars,0,3);//写入数组的指定部分
fileWriter.write("你牛逼bee",0,3);//写入字符串的指定部分
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
fileWriter.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
9.节点流、包装流
package writer__;
public class FileReader_ extends Reader_{
public void readFile(){
System.out.println("读取文件·····");
}
}
2、
package writer__;
public class StringReader_ extends Reader_{
public void readString(){
System.out.println("读取字符串····");
}
}
package writer__;
public abstract class Reader_ {
public void readFile(){
}
public void readString(){
}
}
4.BufeerReader—继承Reader_,并重写父类的方法
package writer__;
public class BufferReader_ extends Reader_{
private Reader_ reader_;
public BufferReader_(Reader_ reader_){
this.reader_ = reader_;
}
//重写父类Reader_的方法
public void readFile(int num){//扩展共功能,多次读取文件
for (int i = 0; i < num; i++) {
reader_.readFile();
}
}
public void readString(int num){
for (int i = 0; i < num; i++) {
reader_.readString();
}
}
}
5.测试类
package writer__;
import java.io.BufferedReader;
import java.nio.Buffer;
public class Test_ {
public static void main(String[] args) {
BufferReader_ bufferReader_ = new BufferReader_(new FileReader_());
bufferReader_.readFile(10);//调用BufferedReader_中重写的方法
//这次希望通过BufferReader_多次读取字符串
BufferReader_ bufferReader_1 = new BufferReader_(new StringReader_());
bufferReader_1.readString(5);//调用BufferedReader_中重写的方法
}
}
10.目录
- **mkdir( )**方法创建一个文件夹,成功则返回true,失败则返回false。失败表明File对象指定的路径已经存在,或者由于整个路径还不存在,该文件夹不能被创建。
- **mkdirs()**方法创建一个文件夹和它的所有父文件夹。
读取目录:
- 一个目录其实就是一个 File 对象,它包含其他文件和文件夹。
- 如果创建一个 File 对象并且它是一个目录,那么调用
isDirectory
() 方法会返回 true。 - 可以通过调用该对象上的
list
() 方法,来提取它包含的文件和文件夹的列表。
删除目录或文件:
- 删除文件可以使用 delete() 方法。
- 以下代码会删除目录 /tmp/java/,需要注意的是当删除某一目录时,必须保证该目录下没有其他文件才能正确删除,否则将删除失败。
字符串
1.创建字符串
(1)直接创建String s1 = “我是帅哥”;
(2)通过对象创建String s2 = new String(“我不是帅哥”);
2.字符串长度:调用**length()**方法获取字符串长度
3.连接字符串
- concat() 方法:
let str1 = 'hello';
let str2 = 'world';
let str3 = '!';
let newStr = str1.concat(str2, str3); // 'helloworld!'
+
号直接连接:“hello”+“work”+“!”;
4.创建格式化字符串
System.out.printf("浮点型变量的值为 " +
"%f, 整型变量的值为 " +
" %d, 字符串变量的值为 " +
"is %s",
floatVar, intVar, stringVar);
或者
String fs;
fs = String.format("浮点型变量的值为 " +"%f, 整型变量的值为 " +" %d, 字符串变量的值为 " +"%s", floatVar, intVar, stringVar);
String 方法
下面是 String 类支持的方法,更多详细,参看 Java String API 文档:
String、StringBuffer 和 StringBuilder 类
- 可变性 : String类中使用字符数组保存字符串,所以string对象是不可变 的。
StringBuilder与StringBuffer这两种对象都是可变的。String是被final修饰的 - 线程安全性 : String中的对象是不可变的,也就可以理解为常量,线程安全。StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。StringBuilder是非线程安全的
- 性能 : 每次对String 类型进行改变的时候,都会生成一个新的String对象,然后将指针指向新的String 对 象。StringBuffer每次都会对StringBuffer对象本身进行操作,而不是生成新的对象并改变对象引用。
StirngBuilder 相比使用StringBuffer而言效率更高 - 总结一下
String:适用于少量的字符串操作的情况
StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况
StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况
public class Test{
public static void main(String args[]){
StringBuffer sBuffer = new StringBuffer("菜鸟教程官网:");
sBuffer.append("www");
sBuffer.append(".runoob");
sBuffer.append(".com");
System.out.println(sBuffer);
}
}
数组
- 数组可以作为函数的参数
- 数组可以作为函数的返回值
一维数组:
1.静态初始化
数组类型 [] 名称 = new 类型{元素};
int [] Array = new int[]{1,2,3};
也可以先声明再初始化:
int [] Array;
Array = new int[]{1,2,3};
2.动态初始化
int [] Array = new int[数组长度];
2)声明二维数组有下列两种格式:
数组的元素类型 数组名[][];
数组的元素类型 [][] 数组名;
//例如
int float boy[];
char cat[][];
4.一维数组的遍历
for(int i=0;i<Array.length;i++) {
System.out.println(Array[i]);
}
二维数组:
1.定义
格式一
int[][] arr = new int[3][2];
格式二
int[][] arr = new int[3][];
格式三
int[][] arr = {{1,2,3},{4,5},{6,7,8,9}};
2.静态初始化
(1)先声明再静态初始化
元素的数据类型[][] 二维数组名;
二维数组名 = new 元素的数据类型[][]{{元素1,元素2,元素3,…},{第二行的值列表},…,{第n行的值列表}};
(2)声明的同时静态初始化
元素的数据类型[][] 二维数组名 = new 元素的数据类型[][]{{元素1,元素2,元素3,…},{第二行的值列表},…,{第n行的值列表}};
(3)声明的同时静态初始化的简化写法
元素的数据类型[][] 二维数组名 = {{元素1,元素2,元素3,…},{第二行的值列表},…,{第n行的值列表}};
public class Main {
public static void main(String[] args) {
int[][] arr = {{1,2,3},{4,5},{6}}; //定义数组
System.out.println(arr[0][0]); //1
System.out.println(arr[1][0]); //4
System.out.println(arr[2][0]); //6
System.out.println(arr[0][1]); //2
System.out.println(arr[1][1]); //5
System.out.println(arr[2][1]); //越界
}
3. 动态初始化
格式一:
元素的数据类型[][] 二维数组名 = new 元素的数据类型[m][n];
public class Main {
public static void main(String[] args) {
int[][] arr = new int[3][2];
/*
定义一个二维数组arr
这个二维数组有3个一维数组的元素
每个一维数组有2个元素
*/
System.out.println(arr); //输出二维数组名称
System.out.println(arr[0]); //输出二维数组的第一个元素一维数组的名称
System.out.println(arr[0][0]); //输出二维数组的元素
}
}
格式二:
//不规则二维表定义二维数组
int[][] arr = new int[3][]; //3行,未知的列
//初始化二维数组内的元素值
arr[0] = new int[]{1}; //此处不能简写为arr[0] = {1}
arr[1] = new int[]{2,3};
arr[2] = new int[]{4,5,6};
4.遍历
for(int i=0;i<二维数组名.length;i++) {
for(int j=0;j<二维数组名[i].length;j++) {
System.out.print(二维数组名[i][j]);
}
System.out.println();
}
循环结构
while 循环
while( 布尔表达式 ) {//若为true,会一直循环
//循环内容
}
do…while 循环
do…while 循环和 while 循环相似,不同的是,do…while 循环至少会执行一次。
do {
//代码语句
}while(布尔表达式);
for循环
for(初始化; 布尔表达式; 更新) {
//代码语句
}
实例:
for(int i=0;i<10;i++){
System.out.print(i);
}
输出:1,2,....
增强 for 循环
-
Java5 引入了一种主要用于数组的增强型 for 循环。
-
声明语句:声明新的局部变量,该变量的类型必须和数组元素的类型匹配。其作用域限定在循环语句块,其值与此时数组元素的值相等。
-
表达式:表达式是要访问的数组名,或者是返回值为数组的方法。
/*for(声明语句 : 数组名)
{
//代码句子
}*/
//实例
int [] numbers = {10, 20, 30, 40, 50};
for(int x : numbers ){
System.out.print( x );
System.out.print(",");
}
输出:10,20,30,40,50
break 关键字
-
break 主要用在循环语句或者 switch 语句中,用来跳出整个语句块。
-
break 跳出最里层的循环,并且继续执行该循环下面的语句。
public class Test {
public static void main(String[] args) {
int [] numbers = {10, 20, 30, 40, 50};
for(int x : numbers ) {
// x 等于 30 时跳出循环
if( x == 30 ) {
break;
}
System.out.print( x );
System.out.print("\n");
}
}
}
输出:
10
20
continue 关键字
-
continue 适用于任何循环控制结构中。作用是让程序立刻跳转到下一次循环的迭代。
-
在 for 循环中,continue 语句使程序立即跳转到更新语句。
-
在 while 或者 do…while 循环中,程序立即跳转到布尔表达式的判断语句。
public class Test {
public static void main(String[] args) {
int [] numbers = {10, 20, 30, 40, 50};
for(int x : numbers ) {
if( x == 30 ) {
continue;
}
System.out.print( x );
System.out.print("\n");
}
}
}
输出:
10
20
40
50
条件语句
if语句
//语法
if(布尔表达式)
{
//如果布尔表达式为true将执行的语句
}
if…else语句
if(布尔表达式){
//如果布尔表达式的值为true
}else{
//如果布尔表达式的值为false
}
if…else if…else 语句
需要注意下面几点:
- if 语句至多有 1 个 else 语句,else 语句在所有的 else if 语句之后。
- if 语句可以有若干个 else if 语句,它们必须在 else 语句之前。
- 一旦其中一个 else if 语句检测为 true,其他的 else if 以及 else 语句都将跳过执行。
语法
if(布尔表达式 1){
//如果布尔表达式 1的值为true执行代码
}else if(布尔表达式 2){
//如果布尔表达式 2的值为true执行代码
}else if(布尔表达式 3){
//如果布尔表达式 3的值为true执行代码
}else {
//如果以上布尔表达式都不为true执行代码
}
嵌套的 if…else 语句
使用嵌套的 if…else 语句是合法的。也就是说你可以在另一个 if 或者 else if 语句中使用 if 或者 else if 语句。
语法
if(布尔表达式 1){
如果布尔表达式 1的值为true执行代码
if(布尔表达式 2){
如果布尔表达式 2的值为true执行代码
}
}
switch case 语句
- switch case 执行时,一定会先进行匹配,匹配成功返回当前 case 的值,再根据是否有 break,判断是否继续输出,或是跳出判断。
- switch case 语句判断一个变量与一系列值中某个值是否相等,每个值称为一个分支。
语法
switch(expression){
case value :
//语句
break; //可选
case value :
//语句
break; //可选
//你可以有任意数量的case语句
default : //default 在没有 case 语句的值和变量值相等的时候执行。default 分支不需要 break 语句。
//语句
}
方法
- **修饰符:**修饰符,这是可选的,告诉编译器如何调用该方法。定义了该方法的访问类型。
- 返回值类型 :方法可能会返回值。returnValueType 是方法返回值的数据类型。有些方法执行所需的操作,但没有返回值。在这种情况下,returnValueType 是关键字void。
- **方法名:**是方法的实际名称。方法名和参数表共同构成方法签名。
- **参数类型:**参数像是一个占位符。当方法被调用时,传递值给参数。这个值被称为实参或变量。参数列表是指方法的参数类型、顺序和参数的个数。参数是可选的,方法可以不包含任何参数。
- **方法体:**方法体包含具体的语句,定义该方法的功能
方法结构:
修饰符 返回值类型 方法名(参数类型 参数名){
...
方法体
...
return 返回值;
}
方法调用
- Java 支持两种调用方法的方式,根据方法是否返回值来选择。
- 当程序调用一个方法时,程序的控制权交给了被调用的方法。当被调用方法的返回语句执行或者到达方法体闭括号时候交还控制权给程序。
- 当方法返回一个值的时候,方法调用通常被当做一个值。如果方法返回值是void,方法调用一定是一条语句。
void 关键字
- 方法是一个void类型方法,它不返回值。
- 一个void方法的调用一定是一个语句。
通过值传递参数
调用一个方法时候需要提供参数,你必须按照参数列表指定的顺序提供。
方法的重载
- 一个类的两个方法拥有相同的名字,但是有不同的参数列表
- 如果你调用max方法时传递的是int型参数,则 int型参数的max方法就会被调用;
- 如果传递的是double型参数,则double类型的max方法体会被调用,这叫做方法重载;
面向对象
构造方法
-
构造器用于初始化对象
-
必须与类同名,一个类可以有多个构造方法。
-
构造器没有返回值,连void都没有
-
每个类都有构造方法。如果没有显式地为类定义构造方法,Java 编译器将会为该类提供一个默认构造方法
示例:
public class Text2{
public Text2(){
}
public Text2(String name){//这是一个构造方法
// 这个构造器仅有一个参数:name
}
}
创建对象
- 声明:声明一个对象,包括对象名称和对象类型。
- 实例化:使用关键字 new 来创建一个对象。
- 初始化:使用 new 创建对象时,会==调用构造方法初始化对象。==
实例:
public class Text2 {
public Text2(String name){//构造方法,形参为name
System.out.println("小狗名字:"+name);
}
public static void main(String[] args) {
Text2 t2= new Text2("luck");
}
}
输出:小狗名字:luck
访问实例变量和调用成员方法
package Text;
public class Puppy{
int puppyAge;
public Puppy(String name){
// 这个构造器仅有一个参数:name
System.out.println("小狗的名字是 : " + name );
}
public void setAge( int age ){
puppyAge = age;
}
public int getAge( ){
System.out.println("小狗的年龄为 : " + puppyAge );
return puppyAge;
}
public static void main(String[] args){
/* 创建对象 */
Puppy myPuppy = new Puppy( "tommy" );
/* 通过方法来设定age */
myPuppy.setAge( 2 );
/* 调用另一个方法获取age */
myPuppy.getAge( );
/*你也可以像下面这样访问成员变量 */
System.out.println("变量值 : " + myPuppy.puppyAge );
}
}
输出:
小狗的名字是 : tommy
小狗的年龄为 : 2
变量值 : 2
- 局部变量:在方法、构造方法或者语句块中定义的变量被称为局部变量。
- 成员变量:成员变量是定义在类中,方法体之外的变量。
- 类变量(静态变量):类变量也声明在类中,方法体之外,但必须声明为 static 类型。
继承
继承的特性:
-
子类拥有父类非 private 的属性、方法。
-
子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
-
子类可以用自己的方式实现父类的方法。
-
Java 的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如 B 类继承 A 类,C 类继承 B 类,所以按照关系就是 B 类是 C 类的父类,A 类是 B 类的父类,这是 Java 继承区别于 C++ 继承的一个特性。
**extends关键字:**类的继承
**implements关键字:**使用 implements 关键字可以变相的使java具有多继承的特性,使用范围为类继承接口的情况,可以同时继承多个接口(接口跟接口之间采用逗号分隔)。
super 与 this 关键字:
super关键字:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。当需要在子类中调用父类的被重写方法时,要使用 super 关键字。
this关键字:指向自己的引用。
final 关键字:
- final 可以用来修饰变量(包括类属性、对象属性、局部变量和形参)、方法(包括类方法和对象方法)和类。
- final 含义为 “最终的”。
- 使用 final 关键字声明类,就是把类定义定义为最终类,不能被继承,或者用于修饰方法,该方法不能被子类重写:
构造器:
- 子类是不继承父类的构造器(构造方法或者构造函数)的,它只是调用(隐式或显式)。如果父类的构造器带有参数,则必须在子类的构造器中显式地通过 super 关键字调用父类的构造器并配以适当的参数列表。
- 如果父类构造器没有参数,则在子类的构造器中不需要使用 super 关键字调用父类构造器,系统会自动调用父类的无参构造器。
重写与重载
**重写(Override):**重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!
方法的重写规则
- 参数列表与被重写方法的参数列表必须完全相同。
- 返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类(java5 及更早版本返回类型要一样,java7 及更高版本可以不同)。
- 访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected。
- 父类的成员方法只能被它的子类重写。
- 声明为 final 的方法不能被重写。
- 声明为 static 的方法不能被重写,但是能够被再次声明。
- 子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。
- 子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。
- 重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。
- 构造方法不能被重写。
- 如果不能继承一个类,则不能重写该类的方法。
重载(Overload)
-
重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。
-
每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。
-
最常用的地方就是构造器的重载。
重载规则:
- 被重载的方法必须改变参数列表(参数个数或类型不一样);
- 被重载的方法可以改变返回类型;
- 被重载的方法可以改变访问修饰符;
- 被重载的方法可以声明新的或更广的检查异常;
- 方法能够在同一个类中或者在一个子类中被重载。
- 无法以返回值类型作为重载函数的区分标准。
封装
- 任何要访问类中私有成员变量的类都要通过这些getter和setter方法。
public class Person{
private String name;
private int age;
public int getAge(){
return age;
}
public String getName(){
return name;
}
public void setAge(int age){
this.age = age;
}
public void setName(String name){
this.name = name;
}
}
this 关键字
this
关键字用来引用当前对象- 总之,
this
关键字可以用来引用当前对象,并且在访问成员变量或调用成员方法、调用构造函数或将当前对象作为参数传递时都非常有用。
- 这里的
this.x
表示当前对象的成员变量x
,而不是方法参数x
。
复制代码public class MyClass {
private int x;
public void setX(int x) {
this.x = x; // 使用 this 关键字访问成员变量 x
}
}
- 调用当前对象的构造函数:在一个类中的构造函数中,可以通过调用另一个构造函数来避免重复代码。使用
this()
调用同一个类中的另一个构造函数。
- 例如:这里的
this(0)
表示调用带有一个整数参数的构造函数。
复制代码public class MyClass {
private int x;
public MyClass() {
this(0); // 调用另一个构造函数
}
public MyClass(int x) {
this.x = x;
}
}
- 将当前对象作为参数传递给其他方法:在Java中,对象作为参数传递时是通过引用传递的。因此,在当前对象需要作为参数传递给其他方法时,可以使用
this
关键字来明确指定。
- 例如:这里的
myMethod(this)
表示将当前对象作为参数传递给myMethod
方法。
复制代码public class MyClass {
private int x;
public void myMethod(MyClass obj) {
// ...
}
public void anotherMethod() {
myMethod(this); // 将当前对象作为参数传递给 myMethod 方法
}
}
super关键字
- super关键字:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。
- this关键字:指向自己的引用。
多态
- 多态是同一个行为具有多个不同表现形式或形态的能力。
- 多态就是同一个接口,使用不同的实例而执行不同操作
多态存在的三个必要条件
- 继承
- 重写
- 父类引用指向子类对象:Parent(父类) p = new Child(子类)();
方法调用细节
- 当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法
- 看new的对象是谁,就执行谁的方法:编译看左边,执行看右边
接口
接口与类相似点:
- 一个接口可以有多个方法。
- 接口文件保存在 .java 结尾的文件中,文件名使用接口名。
- 接口的字节码文件保存在 .class 结尾的文件中。
- 接口相应的字节码文件必须在与包名称相匹配的目录结构中。
接口与类的区别:
- 接口不能用于实例化对象。
- 接口没有构造方法。
- 接口中所有的方法必须是抽象方法,Java 8 之后 接口中可以使用 default 关键字修饰的非抽象方法。
- 接口不能包含成员变量,除了 static 和 final 变量。
- 接口不是被类继承了,而是要被类实现。
- 接口支持多继承。
接口特性
- 接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)。
- 接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量(并且只能是 public,用 private 修饰会报编译错误)。
- 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法。
抽象类和接口的区别
-
抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。
-
抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
-
接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。
-
一个类只能继承一个抽象类,而一个类却可以实现多个接口。
接口声明:
[可见度] interface 接口名称 [extends 其他的接口名] {
// 声明变量
// 抽象方法
}
内部类
定义:Java中,内部类是在一个类的内部定义的类。
- 内部类可以访问其外部类的所有成员变量和方法,包括私有的成员变量和方法。
- 内部类可以分为静态内部类和非静态内部类两种。
- 非静态内部类(也称为成员内部类)是定义在类里面但不是静态的嵌套类。它可以访问外部类的成员变量和方法,并且可以使用
this
关键字引用外部类对象。非静态内部类通常用于实现某个接口或继承某个抽象类。 - 静态内部类(也称为嵌套类)是定义在类里面并且被 static 修饰的类。它不能访问外部类的非静态成员,但可以访问外部类的静态成员。静态内部类通常用于组织和封装一些和外部类关联紧密的功能代码。
- 内部类还可以分为局部内部类和匿名内部类。局部内部类是定义在方法内部的类,只能在该方法内部使用。匿名内部类没有类名,用于实现某个接口或继承某个类,通常只有一个实例。
非静态内部类细节说明:
- 在这个例子中,我们定义了一个
OuterClass
类和一个InnerClass
内部类。在main
方法中,我们首先创建了一个外部类对象outer
,然后使用outer
对象创建了一个内部类对象inner
。最后,我们调用了内部类的innerMethod
方法。 - 这是因为内部类对象必须依附于外部类对象而存在。如果你尝试在没有外部类对象的情况下创建内部类对象,就会出现编译错误。
public class OuterClass {
private int outerVariable = 10;
// 定义一个非静态内部类
public class InnerClass {
public void innerMethod() {
System.out.println("Inner method called");
System.out.println("Outer variable: " + outerVariable);
}
}
public static void main(String[] args) {
// 创建外部类对象
OuterClass outer = new OuterClass();
// 创建内部类对象
OuterClass.InnerClass inner = outer.new InnerClass();
// 调用内部类方法
inner.innerMethod();
}
}
匿名内部类
匿名内部类定义:一个类中使用new创建一个类的对象,而不使用这个类的名字
- 在这个例子中,我们定义了一个
OuterClass
类和一个被static
修饰的InnerClass
静态内部类。在main
方法中,我们直接使用OuterClass.InnerClass
语法创建了一个静态内部类对象inner
。最后,我们调用了内部类的innerMethod
方法。 - 需要注意的是,在访问外部类的成员变量时,由于静态内部类没有依附于任何外部类对象而存在,所以必须将外部类的成员变量改为
static
修饰。
public class OuterClass {
private static int outerVariable = 10;
// 定义一个静态内部类
public static class InnerClass {
public void innerMethod() {
System.out.println("Inner method called");
System.out.println("Outer variable: " + outerVariable);
}
}
public static void main(String[] args) {
// 创建静态内部类对象
OuterClass.InnerClass inner = new OuterClass.InnerClass();
// 调用静态内部类方法
inner.innerMethod();
}
}
GUI
基本的类和方法
界面 | 类、方法 |
---|---|
窗口 | JFrame |
面板 | JLable |
容器 | ContentPane |
bounds | (x,y,宽,高) |
布局 | Layout |
菜单 | JMenuBar |
标题 | Title |
插入图片 | ImageIcon |
关闭模式 | DefaultCloseOperation(3) |
设置界面居中 | AlwaysOnTop |
按钮 | JButton |
监听器
键盘监听:KeyListener
鼠标监听:MouseListener
动作监听:ActionListener(鼠标左键、空格)
动作监听的三种方法:
1.重新创建一个MyActionListener类,执行括号里的代码
JButton jb = new Jbutton();
jb.addActionListener(new MyActionListener());//参数:要执行的代码
2.创建一个匿名类实现ActionListener接口,当按钮被点击时,actionPerformed方法就会被调用
JButton jb = new JButton("登录");
jFrame.add(jb);
jb.addActionListener(new AbstractAction() {
@Override
public void actionPerformed(ActionEvent actionEvent) {
System.out.println("这是使用匿名内部类来实现的");
}
});
3.继承ActionListener接口
键盘监听(KeyListener):
方法 | 效果 |
---|---|
keyPressed | 代表键被按下 |
keyReleased | 代表键被弹起 |
keyTyped | 代表一个按下弹起的组合动作 |
KeyEvent.getKeyCode() | 可以获取当前点下了哪个键 |
JButton jb2=new JButton("ok");//给按钮增加键盘监听
jFrame.add(jb2);
jb2.addKeyListener(new KeyListener() {
@Override
public void keyTyped(KeyEvent keyEvent) {
System.out.println("按下并弹起");
}
@Override
public void keyPressed(KeyEvent keyEvent) {
System.out.println("键被按下");
}
@Override
public void keyReleased(KeyEvent keyEvent) {
System.out.println("键被弹起");
}
});
鼠标监听(MouseListener):
- mouseReleased 鼠标释放
- mousePressed 鼠标按下
- mouseExited 鼠标退出
- mouseEntered 鼠标进入
- mouseClicked 鼠标点击
JButton jb3=new JButton();//给按钮增加鼠标监听
jFrame.add(jb3);
jb3.addMouseListener(new MouseListener() {
@Override
public void mouseClicked(MouseEvent mouseEvent) {
System.out.println("点击鼠标");
}
@Override
public void mousePressed(MouseEvent mouseEvent) {
System.out.println("按下鼠标");
}
@Override
public void mouseReleased(MouseEvent mouseEvent) {
System.out.println("释放鼠标");
}
@Override
public void mouseEntered(MouseEvent mouseEvent) {
System.out.println("鼠标进入");
}
@Override
public void mouseExited(MouseEvent mouseEvent) {
System.out.println("鼠标退出");
}
});
Math类和number类
Math 类位于 java.lang 包,它的构造方法是 private 的,因此无法创建 Math 类的对象,并且 Math 类中的所有方法都是类方法,可以直接通过类名来调用它们。
静态常量
Math 类中包含 E 和 PI 两个静态常量,正如它们名字所暗示的,它们的值分别等于 e(自然对数)和 π(圆周率)。
求最大值、最小值和绝对值
序号 | 方法与描述 |
---|---|
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() 返回一个随机数。 |
方法 | 说明 |
---|---|
static int abs(int a) | 返回 a 的绝对值 |
static long abs(long a) | 返回 a 的绝对值 |
static float abs(float a) | 返回 a 的绝对值 |
static double abs(double a) | 返回 a 的绝对值 |
static int max(int x,int y) | 返回 x 和 y 中的最大值 |
static double max(double x,double y) | 返回 x 和 y 中的最大值 |
static long max(long x,long y) | 返回 x 和 y 中的最大值 |
static float max(float x,float y) | 返回 x 和 y 中的最大值 |
static int min(int x,int y) | 返回 x 和 y 中的最小值 |
static long min(long x,long y) | 返回 x 和 y 中的最小值 |
static double min(double x,double y) | 返回 x 和 y 中的最小值 |
static float min(float x,float y) | 返回 x 和 y 中的最小值 |
求整运算
方法 | 说明 |
---|---|
static double ceil(double a) | 返回大于或等于 a 的最小整数 |
static double floor(double a) | 返回小于或等于 a 的最大整数 |
static double rint(double a) | 返回最接近 a 的整数值,如果有两个同样接近的整数,则结果取偶数 |
static int round(float a) | 将参数加上 1/2 后返回与参数最近的整数 |
static long round(double a) | 将参数加上 1/2 后返回与参数最近的整数,然后强制转换为长整型 |
指数运算
指数的运算包括求方根、取对数及其求 n 次方的运算。在 Math 类中定义的指数运算方法及其说明如表 4 所示。
方法 | 说明 |
---|---|
static double exp(double a) | 返回 e 的 a 次幂 |
static double pow(double a,double b) | 返回以 a 为底数,以 b 为指数的幂值 |
static double sqrt(double a) | 返回 a 的平方根 |
static double cbrt(double a) | 返回 a 的立方根 |
static double log(double a) | 返回 a 的自然对数,即 lna 的值 |
static double log10(double a) | 返回以 10 为底 a 的对数 |
equals() 方法
- equals() 方法用于判断 Number 对象与方法的参数进是否相等。
语法
public boolean equals(Object o)
参数
o – 任何对象。
返回值
如 Number 对象不为 Null,且与方法的参数类型与数值都相等返回 True,否则返回 False。
基础语法
注释
- 行内注释//
- 多行注释/* */
- 文档注释/** */ —javadoc生成帮助文档
javadoc 标签
javadoc 工具软件识别以下标签:
标签 | 描述 | 示例 |
---|---|---|
@author | 标识一个类的作者 | @author description |
@deprecated | 指名一个过期的类或成员 | @deprecated description |
{@docRoot} | 指明当前文档根目录的路径 | Directory Path |
@exception | 标志一个类抛出的异常 | @exception exception-name explanation |
{@inheritDoc} | 从直接父类继承的注释 | Inherits a comment from the immediate surperclass. |
{@link} | 插入一个到另一个主题的链接 | {@link name text} |
{@linkplain} | 插入一个到另一个主题的链接,但是该链接显示纯文本字体 | Inserts an in-line link to another topic. |
@param | 说明一个方法的参数 | @param parameter-name explanation |
@return | 说明返回值类型 | @return explanation |
@see | 指定一个到另一个主题的链接 | @see anchor |
@serial | 说明一个序列化属性 | @serial description |
@serialData | 说明通过writeObject( ) 和 writeExternal( )方法写的数据 | @serialData description |
@serialField | 说明一个ObjectStreamField组件 | @serialField name type description |
@since | 标记当引入一个特定的变化时 | @since release |
@throws | 和 @exception标签一样. | The @throws tag has the same meaning as the @exception tag. |
{@value} | 显示常量的值,该常量必须是static属性。 | Displays the value of a constant, which must be a static field. |
@version | 指定类的版本 | @version info |
异常处理
序号 | 方法及说明 |
---|---|
1 | public String getMessage() 返回关于发生的异常的详细信息。这个消息在Throwable 类的构造函数中初始化了。 |
2 | public Throwable getCause() 返回一个 Throwable 对象代表异常原因。 |
3 | public String toString() 返回此 Throwable 的简短描述。 |
4 | public void printStackTrace() 将此 Throwable 及其回溯打印到标准错误流。。 |
5 | public StackTraceElement [] getStackTrace() 返回一个包含堆栈层次的数组。下标为0的元素代表栈顶,最后一个元素代表方法调用堆栈的栈底。 |
6 | public Throwable fillInStackTrace() 用当前的调用栈层次填充Throwable 对象栈层次,添加到栈层次任何先前信息中。 |
捕获异常
- 使用 try 和 catch 关键字可以捕获异常。
try/catch
代码块放在异常可能发生的地方。
try/catch代码块中的代码称为保护代码,使用 try/catch 的语法如下:
try
{
// 可能出现异常的程序代码
}catch(ExceptionName e1)
{
//Catch 块
}
多重捕获块:
try{
// 程序代码
}catch(异常类型1 异常的变量名1){
// 程序代码
}catch(异常类型2 异常的变量名2){
// 程序代码
}catch(异常类型3 异常的变量名3){
// 程序代码
}
throws/throw 关键字:
-
如果一个方法没有捕获到一个检查性异常,那么该方法必须使用 throws 关键字来声明。throws 关键字放在方法签名的尾部。
-
也可以使用
throw
关键字抛出一个异常,无论它是新实例化的还是刚捕获到的。
声明自定义异常
所有异常都必须是 Throwable 的子类。
如果希望写一个检查性异常类,则需要继承 Exception 类。
如果你想写一个运行时异常类,那么需要继承 RuntimeException 类。
集合
集合框架体系
背下来
如何选择集合
Iterator迭代器
实现子接口
- Collection
- Map
性质
-
iterator接口的所有子接口都可以使用迭代器
-
Java集合框架中的一个接口,它提供了一种访问集合类(Collection)中元素的方法,而不需要暴露该集合的底层实现。
-
Iterator迭代器的主要作用就是为了遍历集合中的元素。它定义了三个方法:hasNext()、next()和remove()。
- hasNext()用于判断集合中是否还有下一个元素;
- next()指针后移,用于获取集合中的下一个元素;
- remove()用于从集合中删除上一个被访问过的元素。
三种遍历方式
第一种方式:使用迭代器while循环
package Collection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
@SuppressWarnings("all")
public class IteratorDemo {
public static void main(String[] args) {
Collection col = new ArrayList();//向上转型
col.add(new Book("撒士大夫","a",12));
col.add(new Book("1山大王","b",12));
col.add(new Book("十大动物","c",12));
Iterator iterator = col.iterator();//得到迭代器
//快捷键itit
while (iterator.hasNext()) {
Object next = iterator.next();
System.out.println(next);
}
//当退出while循环是,iterator指向最后一个元素
//若继续遍历会出现异常:iterator.next():NoSuchElementException
//要想重新遍历需要 重置iterator迭代器
iterator = col.iterator();
System.out.println("第二次遍历");
while (iterator.hasNext()) {
Object next = iterator.next();
System.out.println(next);
}
}
}
class Book{
private String name;
private String author;
private double price;
public Book(String name, String author, double price) {
this.name = name;
this.author = author;
this.price = price;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "Book{" +
"name='" + name + '\'' +
", author='" + author + '\'' +
", price=" + price +
'}';
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}
第二种方式:使用增强For循环
- for循环的本质就是简化版的迭代器
import java.util.ArrayList;
import java.util.Collection;
public class iteratorFor {
public static void main(String[] args) {
Collection col = new ArrayList();//向上转型
col.add(new Book("撒士大夫","a",12));
col.add(new Book("1山大王","b",12));
col.add(new Book("十大动物","c",12));
//使用增强For循环
for(Object book : col){
System.out.println(book);
}
//快捷键I
}
}
class Book{
private String name;
private String author;
private double price;
public Book(String name, String author, double price) {
this.name = name;
this.author = author;
this.price = price;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "Book{" +
"name='" + name + '\'' +
", author='" + author + '\'' +
", price=" + price +
'}';
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}
第三种方式:使用普通for
//3.使用普通的for
System.out.println("普通for");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
三种方式的使用
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class IteratorTest {
@SuppressWarnings("all")
public static void main(String[] args) {
List list = new ArrayList();
list.add(new Dog("小王",20));
list.add(new Dog("小成",21));
list.add(new Dog("小珍",22));
//1.使用增强for
System.out.println("使用增强for");
for (Object dog : list) {
System.out.println(dog);
}
//2.使用迭代器
System.out.println("使用迭代器");
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
System.out.println(next);
}
//3.使用普通的for
System.out.println("普通for");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
}
class Dog{
private String name;
private int age;
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
}
collection接口
子接口
- List
- Set
性质
- collection的父接口是:iterator(迭代器)
- collection接口的方法,其所有的实现子类都可以使用
collection的方法
- add:添加元素
- addAll:添加多个元素
- remove:指定删除元素
- removeAll:删除多个元素
- contains:查找某个元素是否存在
- containsAll:查找多个元素是否存在
- size:获取元素个数
- isEmpty:判断是否为空
package Collection;
import java.util.ArrayList;
import java.util.List;
@SuppressWarnings({"all"})
public class CollectionDemo {
public static void main(String[] args) {
List list = new ArrayList();
//add:添加元素
list.add("okok");
list.add(999);
list.add(true);
System.out.println("list="+list);
//remove:指定删除元素
list.remove("okok");
list.remove(1);
System.out.println("list="+list);
//contains:查找某个元素是否存在
System.out.println(list.contains(999));
//size:获取元素个数
System.out.println(list.size());
//isEmpty:判断是否为空
System.out.println(list.isEmpty());
//clear:清空
list.clear();
System.out.println("list="+list);
//addAll:添加多个元素
List list2 = new ArrayList();
list2.add("牛马");
list2.add("黑马");
list.add("斑马");
list.addAll(list2);
System.out.println(list);
//containsAll:查找多个元素是否存在
System.out.println(list.containsAll(list));
//removeAll:删除多个元素
list.removeAll(list);
System.out.println(list);
}
}
List接口(有序,可重复)
子接口
- Array List
- Vector
- Linked List
性质
-
List实现了Collection接口,可以使用Collection的所有方法
-
List的所有实现子类都是元素有序,且可重复
-
支持索引
List 方法
- add():添加元素
- add(index," "):插入元素,指定位置
- addAll():添加所有元素
- indexOf():返回元素第一次出现的位置
- lastIndexOf():返回**元素最后出现的位置****
- remove(index):删除指定index位置的元素
- set(index," value"):替换元素
- subList(int fromIndex , int toIndex):返回fromIndex到toIndex的元素
注:左闭右开(fromIndex<= subList <toIndex),fromIndex ~ toIndex-1
import java.util.ArrayList;
import java.util.List;
public class List_ {
@SuppressWarnings({"all"})
public static void main(String[] args) {
List list = new ArrayList();
// 1. add():添加元素
list.add("你哈");
list.add("回家了");
list.add("老刘");
list.add("老刘");
System.out.println(list);
// 2. add(index," "):插入元素,指定位置
list.add(1,"卧槽");
System.out.println(list);
// 3. addAll():添加所有元素
List list2 = new ArrayList();
list2.add("浓缩咖啡");
list2.add("而疯狂");
list2.add("二秒汪峰");
list.addAll(list2);
System.out.println(list);
// 4. indexOf():返回元素**第一次**出现的位置
System.out.println(list.indexOf("老刘"));
// 5. lastIndexOf():返回**一个元素**最后出现的位置
System.out.println(list.lastIndexOf("老刘"));
// 6. remove(index):删除指定index位置的元素
list.remove(4);
System.out.println(list);
// 7. set(index," value"):替换元素
list.set(1,"解决");
System.out.println(list);
// 8. subList(int fromIndex , int toIndex):返回fromIndex到toIndex的元素
System.out.println(list.subList(0,5));//0~5-1
// 注:左闭右开(fromIndex<= subList <toIndex)
}
}
List排序练习
package List;
import java.util.ArrayList;
import java.util.List;
@SuppressWarnings({"all"})
public class ListBook {
public static void main(String[] args) {
List list = new ArrayList();
list.add(new Book("可多了",20));
list.add(new Book("范德萨",30));
list.add(new Book("色二点",10));
for (Object o : list) {
System.out.println(o);
}
System.out.println("排序后");
sort(list);
for (Object o : list) {
System.out.println(o);
}
}
//冒泡排序
public static void sort(List list){
int listSize = list.size();
for (int i = 0; i < listSize - 1; i++) {
for(int j = 0; j < listSize - 1-i;j++){
Book book1 = (Book) list.get(j);
Book book2 = (Book) list.get(j+1);
if (book1.getPrice()>book2.getPrice()){
list.set(j,book2);
list.set(j+1,book1);
}
}
}
}
}
class Book{
private String name;
private int price;
@Override
public String toString() {
return "书名:"+name+"\t\t价格:"+price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public Book(String name, int price) {
this.name = name;
this.price = price;
}
}
Array List
性质
- 底层使用数组实现的
- 排列有序,可以存放任何的值,包括空值(null),可重复
- 线程不安全
- 改查效率高,增删的效率低
扩容
- 当创建Array List对象时(使用的是无参构造器):初始化element Data容量为0,第一次添加就element Data扩容为10,再次扩容为element Data的1.5倍 (0 ~ 10 ~ 1.5倍)
- 当创建Array List对象时(使用的是有参构造器),即创建时指定容量:当扩容时,就扩容到1.5倍 (指定容量 ~ 1.5倍)
- 当扩容后满足所需要的空间,如果有剩余的空间会置为空(null)
package List;
import java.util.ArrayList;
@SuppressWarnings("all")
public class ArrayListYM {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
for (int i = 0; i < 13; i++) {
arrayList.add("hello"+i);
}
for (Object o : arrayList) {
System.out.println(o);
}
}
}
Vector
性质
- Vector是List的实现子类,排列有序,可以存放任何的值,包括空值(null),可重复
- 线程同步,且线程安全(操作方法带有synchronized)
- Vector底层是一个对象数组
扩容
- 若使用无参构造,则默认是10,满后按2倍扩容
- 若使用有参构造,则指定大小,满后按2倍扩容
package List;
import java.util.Vector;
@SuppressWarnings("all")
public class Vector_ {
public static void main(String[] args) {
Vector vector = new Vector();
for (int i = 0; i < 10; i++) {
vector.add("hello"+i);
}
vector.add(200);
for (Object o : vector) {
System.out.println(o);
}
}
}
Linked List
性质
- Linked List是List的实现子类
- 排列有序,可以存放任何的值,包括空值(null),可重复
- 线程不安全
- 增删的效率高,改查的效率低
底层
- Linked List底层实现一个双向链表
- Linked List中维护两个属性first和last分别执行首节点和尾节点
- 每个Node对象里面又维护了prev、next、item三个属性
- prev:指向前一个
- next:指向后一个
- item:节点数据
Set接口(元素不可重复)
子接口
- Hash Set
- Linked Hash Set
- Tree Set
性质
-
Set是Collection的实现子接口
-
不可重复,且最多只能含有一个null值
-
无序:即添加的顺序和取出顺序不一致
-
set接口对象,不能使用索引来获取(即不能使用普通的for循环来遍历,因为Set接口没有提供get()方法)
Hash Set
性质
- Hash Set是Set的实现子类
- 元素不可重复,只能有一个null
底层
- Hash Set底层是Hash Map(哈希表:数组+链表)
方法
-
add:添加元素:先得到hash值——>索引值(转成)
-
但不能添加相同对象的数据
-
底层调用equals()方法比较,是否指向同一个对象
- String指向同不同的对象,可以添加相同的元素
- 其它的类,就按照其它的标准实现(重写equals)
-
当一条链表节点达到了8个节点,并且table(哈希表)的大小>=64时,会转化为红黑树
-
一条链表节点达到了8个节点,但table(哈希表)的大小小于64时,则会扩容,不会树化
-
扩容机制
- 第一次添加时table数组扩容到16,临界值为容量的0.75倍(加载因子:16*0.75)
- 如果table数组使用到了临界值12时,就会扩容到2倍(16*2),然后再算新的临界值
HashMap去重机制
- hashCode()+equals(),底层先通过存入对象,运算得到hash值,通过hash值得到对应的索引,
- 如果table索引存在的位置,没有数据,就直接放入;
- 否则就进行equals()比较;如果比较后,不相同就放入,否则,就不加入
Hash Set练习,以及equals方法的理解实现
package Set;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
/**
* HashSet的练习,理解new的对象是否一样,理解equals方法
* 目标:当name和age一样时,表示同一个,不能加入
*/
@SuppressWarnings("all")
public class HashSet_ {
public static void main(String[] args) {
Set hashSet = new HashSet();
hashSet.add(new Employee("join",30));//T
hashSet.add(new Employee("jack",20));//T
hashSet.add(new Employee("lucy",23));//T
hashSet.add(new Employee("join",30));//F
for (Object o : hashSet) {
System.out.println(o);
}
}
}
class Employee{
private String name;
private int age;
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Employee(String name, int age) {
this.name = name;
this.age = age;
}
//当name和age一样时,不能加入,则返回相同的hash值,
//重写equals方法 快捷键:Alt+insert
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Employee employee = (Employee) o;
return age == employee.age && name.equals(employee.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
Linked Hash Set
性质
- LinkedHashSet是Set的实现子类
- LinkedHashSet和Hash的区别是:LinkedHashSet底层是一个LinkedHashMap,底层维护一个数组+双向链表
- 每一个节点有一个prev和next指针
- 每一个节点有一个before和after的属性
- Linked Hash Set是根据元素的hashCode值来决定元素的存储位置,同时使用链表维护元素的次序,这使得元素看起来是以插入的顺序保存的
- Linked Hash Set不允许添加重复元素
- Linked Hash Set是有序
Linked Hash Set练习
package Set;
import java.util.LinkedHashSet;
import java.util.Objects;
@SuppressWarnings("all")
public class LinkedHashSet_ {
public static void main(String[] args) {
LinkedHashSet linkedHashSet = new LinkedHashSet();
linkedHashSet.add(new Car("奥迪", 2000000));//ok
linkedHashSet.add(new Car("奥迪", 2000000));//默认是ok,但重写equals就不能加入了
linkedHashSet.add(new Car("宝马", 300000));
linkedHashSet.add(new Car("兰博基尼", 43100000));
linkedHashSet.add(new Car("法拉利", 2001210000));
System.out.println("LinkedHashSet=" + linkedHashSet);
}
}
class Car {
private String name;
private int price;
//重写equals和hashcode
//但name和price一样时,返回相同的hashcode值,
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Car car = (Car) o;
return price == car.price && Objects.equals(name, car.name);
}
@Override
public int hashCode() {
return Objects.hash(name, price);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public Car(String name, int price) {
this.name = name;
this.price = price;
}
@Override
public String toString() {
return "\nCar{" +
"name='" + name + '\'' +
", price=" + price +
'}';
}
}
Tree Set
性质
-
Tree Set的底层是Tree Map
-
可以用于排序
-
使用无参构造器是无序的
TreeSet去重机制
- 如果你传入了一个Comparator匿名对象,就使用compare去重
- 如果方法返回0,就认为是相同的元素,就不添加;
- 如果没有传入一个Comparator匿名对象,则以你添加的对象实现的Copmpartator接口的compareTo去重
package Set;
import java.util.Comparator;
import java.util.TreeSet;
@SuppressWarnings("all")
public class TreeSet_ {
public static void main(String[] args) {
//使用TreeSet提供的一个构造器,可以传入一个比较器
TreeSet treeSet = new TreeSet(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
//调用String的compareTo方法比较
//按照自己的需求来改写return的语句
// return ((String)o2).compareTo((String)o1);//排序
return ((String) o1).length() - ((String) o2).length();//长度一样的加入不
}
});
treeSet.add("join");
treeSet.add("mary");
treeSet.add("luc");
treeSet.add("ms");
System.out.println(treeSet);
}
}
Map接口
特点:双列元素(键值对)
实现子类
- Hash table
- Hash Map
- Tree Map
性质
- Map与Collection并列存在,用于保存具有映射关系的数据Kay-Value
- Map中的元素是:无序
- Map中的Key不允许重复,原因和Hash Set一样,
- 但有相同Key是,会替换
- key只能有一个,Value可以有多个重复
- Map中的Value可以重复
- 一对k-v是放在一个HashMap$Node中的,是因为Node实现了Entry接口
- 常用String类作为Map的Key
常用方法
- put(): 添加
- get(key):根据输入key得到对应value
- remove(key):删除键值对k-v
- size():获取元素的个数
- isEmpty():判断个数是否为0
- clear():清除键值对k-v
- containsKey(key):查看键是否存在
Map遍历
-
第一组:通过Set,取出所有的key,再通过key取出value
-
第二组:取出所有的Value(只有values,没有key)
-
第三组:通过Entry Set获取k-v
- 增强for
- 迭代器
遍历的实现
package Map;
import java.util.*;
@SuppressWarnings("all")
public class Mapfor {
public static void main(String[] args) {
Map hashMap = new HashMap();
hashMap.put(1,"dog");
hashMap.put("王宝强","马蓉");
hashMap.put("牛马","傻鸟");
hashMap.put("dog","小王");
hashMap.put("pig","小c");
// 第一组:取出所有的key,再通过key取出value
Set keyset = hashMap.keySet();//重点
// 1. 增强for
System.out.println("------第一组增强for---------");
for (Object key : keyset) {
System.out.println(key+"-"+hashMap.get(key));
}
// 2. 迭代器
System.out.println("------第一组迭代器---------");
Iterator iterator = keyset.iterator();
while (iterator.hasNext()) {
Object key = iterator.next();
System.out.println(key+"-"+hashMap.get(key));
}
// 第二组:取出所有的Value
Collection values = hashMap.values();
// 1. 增强for
System.out.println("------第二组增强for---------");
for (Object val : values) {
System.out.println(val);
}
// 2. 迭代器
System.out.println("------第二组迭代器---------");
Iterator iterator02 = values.iterator();
while (iterator02.hasNext()) {
Object val = iterator02.next();
System.out.println(val);
}
// 第三组:通过Entry Set获取k-v
Set entrySet = hashMap.entrySet();//重点
// 1. 增强for
System.out.println("------第三组组增强for---------");
for (Object o : entrySet) {
System.out.println(o);
}
// 2. 迭代器
System.out.println("------第三组组迭代器---------");
Iterator iterator03 = entrySet.iterator();
while (iterator03.hasNext()) {
Object entry = iterator03.next();
System.out.println(entry);
}
}
}
方法的使用:
package Map;
import java.util.HashMap;
import java.util.Map;
@SuppressWarnings("all")
public class MapApi {
public static void main(String[] args) {
Map map = new HashMap();
// 1. put(): 添加
map.put(1,"dog");
map.put("王宝强","马蓉");
map.put("me","美女");
System.out.println(map);
// 2. get(key):根据输入key得到对应value
System.out.println(map.get("me"));
// 3. remove(key):删除键值对k-v
map.remove("王宝强");
System.out.println(map);
// 4. size():获取元素的个数
System.out.println(map.size());
// 5. isEmpty():判断个数是否为0
System.out.println(map.isEmpty());
// 6. clear():清除键值对k-v
map.clear();
System.out.println(map);
// 7. containsKey(key):查看键是否存在
System.out.println(map.containsKey("me"));
// 8. containsValue(Value):查看值是否存在
System.out.println(map.containsValue("美女"));
}
}
Hash Map
性质
-
Hash Map是Map接口实现子类
-
Hash Map是以键值对k-v的方式来存储数据
-
key不能重复,value可以重复,允许null值
-
如果添加相同的key,会覆盖,等价于替换(key不会替换,value会替换)
-
与Hash Set一样,不保证映射的顺序,因为底层是以hash表来存储的
-
Hash Map没有实现同步,因此线程是不安全的
底层:看Hash Set的部分,Hash Set的底层是Hash Map
Hash table
性质
- 存放元素是键值对:k-v
- Hash table的键和值不允许为null
- hash table是线程安全的
- hash table的使用方法与hash Map基本一样
- 底层有数组Hashtable$Entry[ ]
扩容
- 初始化为11
- 临界值为:容量*0.75
- table的大小>=临界值会进行扩容
- 按照 当前容量*2 +1来扩容
Properties
性质
- Properties类继承自Hash table接口并且实现了Map接口,也是通过键值对的方式保存数据
- Properties还可以用于从xxx.Properties文件中,加载数据到Properties类对象,并进行读取和修改
- xxx.Properties文件通常作为配置文件
- 使用特点与Hash table类似
- Properties类继承自Hash table接口,所以键和值不能为null
- 如果有相同的key,也会被替换
方法
-
put:添加元素
-
get(key):获取对应的value
-
remove(key):删除
package Map; import java.util.Properties; public class Properties_ { public static void main(String[] args) { Properties properties = new Properties(); // put:添加元素 properties.put(1,"wodefuck"); properties.put(1,"shabi");//会覆盖相同的key的value properties.put(3,"wdima"); properties.put(4,"wcao"); System.out.println(properties); // get(key):获取对应的value System.out.println(properties.get(1)); System.out.println(properties.get(2)); // remove(key):删除 properties.remove(4); System.out.println(properties); } }
Tree Map
- Tree Map是Map接口的子类
性质
- 使用无参构造器,仍然是无序的
- 参数传入一个compartor内部类,才能排序
package Map;
import java.util.Comparator;
import java.util.TreeMap;
@SuppressWarnings("all")
public class TreeMap_ {
public static void main(String[] args) {
TreeMap treeMap = new TreeMap(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
//按照key的长度排序
// return ((String)o1).length() - ((String)o2).length();
//按照大小排序,Ascll码的比较
return ((String)o1).compareTo((String)o2);
}
});
treeMap.put("jack","杰克");
treeMap.put("maryl","玛丽");
treeMap.put("sb","傻逼");
treeMap.put("nim","尼玛");
System.out.println(treeMap);
}
}
Collection工具类
以下方法皆为static,通过Collection调用
- reverse(List):反转List中元素的顺序
- shuffle(List):对List集合元素进行随机排序
- sort(List):自然排序,对List元素按升序排列,(Ascll码的大小排序)
- swap(List,i,j):将指定List集合中的
i
处元素与j
处元素交换 - max(List):根据元素的自然顺序,返回集合最大的元素
- 可使用比较器(Comparator)根据需求来重写
- min(List):与max相反
- frequency(List,value):放回元素value在List中出现的次数
- copy(desc,List):将List中的内容复制到desc中
- 需要先给desc赋值,大小与List一样
- replaceAll(List,“oldVal”,“newVal”):用newVal替换List集合中的oldVal的值
package Collection;
import java.util.ArrayList;
import java.util.Collections;
@SuppressWarnings("all")
public class CollectionGJL {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
arrayList.add("jsdla");
arrayList.add("liu");
arrayList.add("cao");
arrayList.add("ma");
arrayList.add("sb");
arrayList.add("sb");
// reverse(List):**反转**List中元素的顺序
Collections.reverse(arrayList);
System.out.println("反转"+arrayList);
// shuffle(List):对List集合元素进行**随机排序**
for (int i = 0; i < 5; i++) {
Collections.shuffle(arrayList);
System.out.println("随机排序"+arrayList);
}
// sort(List):自然排序,对List元素按**升序**排列,(Ascll码的大小排序)
Collections.sort(arrayList);
System.out.println("自然排序"+arrayList);
// swap(List,i,j):将指定List集合中的`i`处元素与`j`处元素交换
Collections.swap(arrayList,0,3);
System.out.println("swap:"+arrayList);
// max(List):根据元素的自然顺序,**返回集合最大的元素**
// 可使用比较器(Comparator)根据需求来重写
System.out.println(Collections.max(arrayList));
// min(List):与max相反
System.out.println(Collections.min(arrayList));
// frequency(List,value):返回元素value在List中出现的次数
System.out.println(Collections.frequency(arrayList,"sb"));
// copy(desc,List):将List中的内容复制到desc中
// 需要先给desc赋值,大小与List一样
ArrayList desc = new ArrayList();
for (int i = 0; i < arrayList.size(); i++) {
desc.add(i);
}
Collections.copy(desc,arrayList);
System.out.println(desc);
// replaceAll(List,"oldVal","newVal"):用newVal替换List集合中的oldVal的值
Collections.replaceAll(arrayList,"cao","你好");
System.out.println(arrayList);
}
}
自定义排序
List.sort(List, new Comparator):指定某个List使用
或者sort(new Comparator)
package FX;
import java.util.ArrayList;
import java.util.Comparator;
@SuppressWarnings("all")
public class Sort_ {
public static void main(String[] args) {
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add("skfkskdf");
arrayList.add("zzzzz");
arrayList.add("aaa");
arrayList.add("woekw");
arrayList.sort(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.length() - o2.length();
}
});
System.out.println(arrayList);
}
}
泛型
泛型是一种接受数据类型的数据类型
使用泛型好处
- 编译时,检查添加元素的类型,提高了安全性
- 减少了类型转化的次数,提高了效率
- 不再提示编译警告
- 使用泛型/不使用泛型的区别
- 不使用泛型(不需要类型转化):加入时Dog->Object ;取出时Object->Dog
- 使用泛型(不需要类型转换):Dog->Dog->Dog
- 使用泛型可以将数据类型参数化,从而可以在使用时指定实际的类型参数,以达到代码复用的目的。
- 泛型可以用在类、接口、方法中
泛型基础
例如,我们可以定义一个泛型类:
kotlinCopy codepublic class MyClass<T> {
private T data;
public void setData(T data) {
this.data = data;
}
public T getData() {
return this.data;
}
}
在上述代码中,我们使用<T>
来定义了一个类型参数,它可以在类的方法中被使用。
例如,setData
方法的参数类型就是T
,返回值类型也是T
当我们实例化这个泛型类时,需要指定一个具体的类型参数。例如:
vbnetCopy codeMyClass<Integer> myClass = new MyClass<>();
myClass.setData(123);
Integer data = myClass.getData();
在上述代码中,我们使用了Integer
作为类型参数,因此setData
方法的参数类型是Integer
,返回值类型也是Integer
。
泛型还可以用在方法中。例如:
cCopy codepublic <T> void printArray(T[] array) {
for (T element : array) {
System.out.println(element);
}
}
在上述代码中,我们使用了<T>
来定义了一个类型参数,它可以在方法中被使用。
当我们调用这个方法时,需要指定一个具体的类型参数。例如:
scssCopy codeInteger[] array = {1, 2, 3};
printArray(array);
在上述代码中,我们指定了Integer
作为类型参数,因此printArray
方法的参数类型是Integer[]
。
泛型使用细节
-
给泛型指向数据类型,要求是引用类型,不能是基本数据类型
List<Integer> list1 = new ArrayList<Integer>();//ok List<int> list1 = new ArrayList<int>();//这种是不能,不能传基本数据类型
-
在给泛型指定具体类型后,可以传入该类型或者其子类型
-
简写(编译;会进行类型推断),当左边指定类型后(编译类型),右边可以省略不写(运行类型)
-
当指定类型不写,默认是Object
泛型的练习:
package FX;
import java.util.ArrayList;
import java.util.Comparator;
@SuppressWarnings("all")
public class Employee {
public static void main(String[] args) {
ArrayList<Emp> emps = new ArrayList<>();
emps.add(new Emp("jack",10,new MyDate(1900,10,21)));
emps.add(new Emp("mays",20,new MyDate(1990,12,1)));
emps.add(new Emp("me",111000,new MyDate(2003,1,19)));
System.out.println(emps);
System.out.println("按照年龄,生日排序");
//自定义排序
emps.sort(new Comparator<Emp>() {
@Override
public int compare(Emp emp1, Emp emp2) {
//先比较年龄
int i = emp1.getName().compareTo(emp2.getName());
if (i!=0){
return i;
}
//年龄相同,再比较年
int year = emp1.getBirthday().getYear() - emp2.getBirthday().getYear();
if (year!=0){
return year;
}
//年相同,再比较月
int month = emp1.getBirthday().getMonth() - emp2.getBirthday().getMonth();
if (month!=0){
return month;
}
//最后比较日
return emp1.getBirthday().getDay() - emp2.getBirthday().getDay();
}
});
System.out.println("自定义排序后");
System.out.println(emps);
}
}
class Emp{
private String name;
private int sal;
private MyDate birthday;
public Emp(String name, int sal, MyDate birthday) {
this.name = name;
this.sal = sal;
this.birthday = birthday;
}
public MyDate getBirthday() {
return birthday;
}
public void setBirthday(MyDate birthday) {
this.birthday = birthday;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getSal() {
return sal;
}
public void setSal(int sal) {
this.sal = sal;
}
@Override
public String toString() {
return "\nEmp{" +
"name='" + name + '\'' +
", sal=" + sal +
", birthday=" + birthday +
'}';
}
}
class MyDate{
private int year;
private int month;
private int day;
public MyDate(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
public int getMonth() {
return month;
}
public void setMonth(int month) {
this.month = month;
}
public int getDay() {
return day;
}
public void setDay(int day) {
this.day = day;
}
@Override
public String toString() {
return "MyDate{" +
"year=" + year +
", month=" + month +
", day=" + day +
'}';
}
}
自定义泛型类
基本语法
class 类名<T,R...>{//可有多个泛型
成员
}
使用细节
- 普通成员可以使用泛型(属性,方法)
- 使用泛型的数组,不能初始化
- 因为数组在new时,不能确定其类型
- 静态方法,不能使用泛型
- 因为静态是与类相关的,在类加载时,对象还没有创建,泛型类型是在对象创建时才确定类型
- 使用泛型时,不指定类型,默认时Objict
- 泛型类的类型,是在创建对象时确定的,因为创建对象时,需要指定确定类型
class Tiger<T,R>{//泛型类
// 1. 普通成员可以使用泛型(属性,方法)
T t;
R r;
// 2. 使用泛型的数组,不能初始化
// - 因为数组在new时,不能确定其类型
T arr[] = new T[9];//NO
public static T nb(){
// 3. 静态方法,不能使用泛型
// - 因为静态是与类相关的,在类加载时,对象还没有创建,泛型类型是在对象创建时才确定类型
}
}
自定义泛型接口
基本语法
interface 接口名<T,R....>{//可有多个泛型
}
注意细节
- 接口中,成员也不能使用泛型(与泛型类规定一样)
- 泛型接口的类型,在继承接口或者实现接口时确实
- 没有指定类型,默认时Object
interface SDjj<S,D>{
// 1. 接口中,**成员**也不能使用泛型(与泛型类规定一样)
// S s ;//NO
int a= 10;
double d = 10.12;
S get(D d);
}
// 2. 泛型接口的类型,在==继承接口==或者==实现接口==时确实
interface DD extends SDjj<String,Double>{
}
class Ts implements DD{
@Override
public String get(Double aDouble) {
return null;
}
}
//3. 没有指定类型,默认时Object
interface Ss extends SDjj{
}
//或者
interface SS extends SDjj<Object,Object>{
}
自定义泛型方法
基本语法
修饰符 <T.R...>返回类型 方法名(参数类型){
}
注意细节
-
泛型方法,可以定义在普通的类中,也可以定义在泛型类中
public<T,R> void aa<T t,R r>{//泛型方法 } //或者 public<E> class bb{//泛型类 public<T,R> void aa{//泛型方法 } public void nn{//普通方法 } }
-
当泛型方法被调用时,类型会确定
- 调用方法时,传入参数,编译器会自动确定类型
public<T,R> void aa<T t,R r>{ } //调用方法 Car car= new Car(); car.aa();
-
泛型方法,可以使用类声明的泛型,也可以使用自己声明的泛型
public<E> class bb{//泛型类 public<T,R> void aa<T t ,R r>{//泛型方法,使用自己声明的泛型 } public<E> void aa<E e>{//泛型方法,使用类声明的泛型 } }
注意
public<E> void aa<E e>{
}
public void aa<E e>{//这个不是泛型方法,只是使用了泛型
}
泛型的继承和通配符
-
泛型没有继承关系
List<Object> list = new ArrayList<String>();//错误
-
<?>
:表示任意的类型都可以接受 -
List<? extends AA>
:表示可以接受AA和AA的子类,规定了泛型的上限 -
List<? super AA>
:表示可以接受AA和AA的父类(不限于直接父类),规定了泛型的下限
网络编程
1、IP地址:InetAddress
- 唯一定位一台计算机
- 127.0.0.1:本机localhost
- IP地址分类:ipv4/ipv6
2、端口:表示计算机上的一个程序的进程,端口号不能冲突
端口号(port)
-
公有端口0~1023
- Http:80
- https;
- FTP:21
- Telent:23
-
程序注册端口:
-
Tocat:8080
-
Mysql:3306
-
Oracle:1521
-
3、通信协议:
- TCP/IP:用户传输协议
- UDP:用户数据协议
- TCP与UDP对比:
TCP:打电话
- 连接,稳定
- 三次握手,四次挥手
- 客户端、服务端
- 传输完成,释放连接、效率低
UDP:发短信
-
不稳定、不连接
-
不管有没有有准备好,都可以发给你
4、socket套接字
- 客户端:
1、连接服务器Socket
2、发送消息
- 服务端:
1、建立服务的端口ServerSocket
2、等待用户的连接accept
3.接受用的消息
InetAddress获取地址的四个方法
package Text;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class InetAddressTest {
public static void main(String[] args) throws UnknownHostException{
//1.获取本机的InnetAddress对象
InetAddress localHost = InetAddress.getLocalHost();
System.out.println(localHost);//输出LAPTOP-3RE9I8E0/192.168.137.1
//2.根据指定主机名获取InetAddress对象
InetAddress host1 = InetAddress.getByName("LAPTOP-3RE9I8E0");
System.out.println("host1="+host1);//host1=LAPTOP-3RE9I8E0/192.168.137.1
//3.根据域名返回InetAddress对象,比如www.baidu.com
InetAddress host2 = InetAddress.getByName("www.baidu.com");
System.out.println(host2);//www.baidu.com/163.177.151.110
//4.通过InetAddress对象,获取对应的地址
String hostAddress = host2.getHostAddress();
System.out.println("host2对应ip="+hostAddress);//www.baidu.com/163.177.151.110
}
}
TCP通信
- 客户端
package Internet;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
public class tcpClientTest {
public static void main(String[] args) {
Socket socket = null;
OutputStream os = null;
try {
//1.要知道服务器的地址和端口号
InetAddress serverIP = InetAddress.getByName("127.0.0.1");
int port = 9999;
//2.创建一个socket连接
socket= new Socket(serverIP,port);
//3.发送消息,使用IO
os = socket.getOutputStream();
os.write("我不喜欢吃香菜".getBytes());
} catch (Exception e) {
throw new RuntimeException(e);
} finally {//关闭流
if(os!=null){
try {
os.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if(socket!=null){
try {
socket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
- 服务端
package Internet;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class tcpServerTest {
public static void main(String[] args) {
ServerSocket serverSocket = null;
Socket socket = null;
ByteArrayOutputStream baos= null;
InputStream is=null;
try {
//1.我需要一个地址
serverSocket= new ServerSocket(9999);
//2,等待客户连接过来
socket = serverSocket.accept();
//3.读取客户端信息
is = socket.getInputStream();
//管道流
baos = new ByteArrayOutputStream();
byte[] buffer= new byte[1024];
int len;
while((len=is.read(buffer))!=-1){
baos.write(buffer,0,len);
}
System.out.println(baos.toString());
} catch (IOException e) {
throw new RuntimeException(e);
} finally {//关闭流
if(baos!=null){
try {
baos.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if(is!=null){
try {
is.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if(socket!=null){
try {
socket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
UDP通信
注解(Annotation)
内置注解:作用镇压警告,
元注解
自定义注解:使用@interface,default设置默认值,
反射(Reflection)
动态语言:javaScript,c#,PHP,Python
静态语言:java,c,c++
反射机制
反射的优点和缺点
- 优点:可以动态的创建和使用对象(也是框架底层核心),使用灵活,没有反射机制,框架就是去底层支撑
- 缺点:使用反射基本是解释执行,对执行速度有影响
Class类
-
Class类也是类,也继承了Object类
-
Class类的对象不是new出来的,而是系统创建的
-
对于某个类的对象,只会在内存存在一份,因为类只会加载一次
-
每个类的对象的实例都会记得自己是哪个Class的实例生成的
-
通过Class对象可以完整的到一个类的完整结果,通过一系列API
-
Class对象是放在堆中的
获取Class类的实例:
package ZJFS;
public class Test02 {
public static void main(String[] args) throws ClassNotFoundException {
Person person = new Student();
System.out.println("这个人是:" + person.name);
//通过对象获得
Class c1 = person.getClass();
System.out.println("c1="+c1.hashCode());
//forname获得
Class c2 = Class.forName("ZJFS.Student");
System.out.println(c2.hashCode());
//通过类名获得
Class c3 = Student.class;
System.out.println("c3="+c3.hashCode());
//本内置包装类型都有一个TYPE属性
Class c4 = Integer.TYPE;
System.out.println("c4="+c4);
//获得父类类型
Class c5= c1.getSuperclass();
System.out.println("c5="+c5);
}
}
class Person{
public String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
class Student extends Person{
public Student(){
this.name = "学生";
}
}
class Teacher extends Person{
public Teacher(){
this.name = "老师";
}
}
获取Class对象
- Class.foName()
- 类名.Class:应用场景,有对象实例
- 对象.getClass()
- 类加载器获取Class对象
- Class cls = 基本数据类型.class
- Class cls = 包装类.TYPE
package Reflection_;
public class Class_duixiang {
public static void main(String[] args) throws ClassNotFoundException {
// 1. Class.foName()
String classAllPath = "Reflection_.Car";
Class<?> cls1 = Class.forName(classAllPath);
System.out.println(cls1);
// 2. 类名.Class:应用场景,有对象实例
Class<Car> cls2 = Car.class;
System.out.println(cls2);
// 3. 对象.getClass()
Car car = new Car();
Class cls3 = car.getClass();
System.out.println(cls3);
// 4. 1.类加载器获取Class对象
//先得到类加载器
ClassLoader classLoader = car.getClass().getClassLoader();
//2,通过类加载器得到calss对象
Class<?> cls4 = classLoader.loadClass(classAllPath);
System.out.println(cls4);
// 5. Class cls = 基本数据类型.class
Class<Integer> integerClass = int.class;
System.out.println(integerClass);
// 6. Class cls = 包装类.TYPE
Class<Integer> type = Integer.TYPE;
System.out.println(type);
}
}
类加载
- 静态加载:编译时加载相关的类,如果没有则报错,依赖性很强
- new 对象时
- 当子类被加载,父类也被加载
- 调用静态成员时
- 动态加载:运行时加载相关的类,如果运行时不用该类,就不会报错,降低了依赖性
- 通过反射
爆破
- 使用反射可以访问private的属性/方法/构造器
通过反射访问类中的成员
- Method m = class.getDeclaredMethod(方法名,XX.class):得到本类中所有的方法
- 获取对象:Object o = class.newInstance()
- 爆破:m.setAccssible(true)
- 访问:Object returnValue = m.invoke(o,实参列表)
- 注意:如果是静态方法,则invoke的参数o,可以写成null
- 在反射中,如果方法中有返回值,统一返回Object
java高级
多线程
注:process(进程):进程是程序的一次执行
Thread(线程):一个进程包含若干个线程
- 真正实现多线程的效果是start()方法,由JVM调用,而不是run()
- 当mian线程启动一个子线程Thread-0,主线程和其他线程一起执行,主线程不会阻塞,会一直执行
1.线程的状态
-
新建状态:
使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。
-
就绪状态:
当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里线程调度器的调度。
-
运行状态:
如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。
-
阻塞状态:
如果一个线程执行了**sleep(睡眠)、suspend(挂起)**等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。可以分为三种:
- 等待阻塞:运行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。
- 同步阻塞:线程在获取 synchronized 同步锁失败(因为同步锁被其他线程占用)。
- 其他阻塞:通过调用线程的 sleep() 或 join() 发出了 I/O 请求时,线程就会进入到阻塞状态。当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。
-
死亡状态:
一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。死亡的线程不能再次开启。
线程休眠(slepp)
- 每个对象都有一把锁,slepp不会释放锁
//模拟倒计时,调用slepp(毫秒)方法,1000毫秒=1秒
package Thread;
public class SleppTest {
public static void main(String[] args) {
try {
tenDwon();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
public static void tenDwon() throws InterruptedException {
int num = 10;
while(true){
Thread.sleep(1000);//Thread类调用slepp()方法
System.out.println(num--);
if (num<=0){
break;
}
}
}
}
线程礼让(yield)
- 礼让线程,让当前线程暂停,但不阻塞
- 礼让不一定成功,由CPU重新调度
package Thread;
//线程礼让
public class YieldTest {
public static void main(String[] args) {
MyYield myYield = new MyYield();
new Thread(myYield,"a").start();
new Thread(myYield,"b").start();
}
}
class MyYield implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程开启");
Thread.yield();//线程礼让
System.out.println(Thread.currentThread().getName()+"线程停止");
}
}
线程强制执行(join)
守护线程
package Thread;
public class DaemonTest {
public static void main(String[] args) {
//创建类的对象
God god = new God();
Kuangshen kuangshen = new Kuangshen();
//开启上帝线程
Thread thread = new Thread(god);
thread.setDaemon(true);//默认是false,默认是用户线程
thread.start();
//开启Kuangshen线程
new Thread(kuangshen).start();
}
}
class God implements Runnable{
@Override
public void run() {
while(true){
System.out.println("上帝守护着你");
}
}
}
class Kuangshen implements Runnable{
@Override
public void run() {
for (int i = 0; i < 300; i++) {
System.out.println("你要好好活着");
}
System.out.println("=========oh,没了==========");
}
}
线程同步(synchronized)
形成条件:队列+锁
-
并发:同一个对象被多个线程同时操作
同步方法:
-
synchronized方法、synchronized块
-
缺陷:占用大量的资源
package Thread;
//模拟买票
public class UserBuyTicket {
public static void main(String[] args) {
BuyTicket buyTicket = new BuyTicket();//创建类的对象
//开启线程
new Thread(buyTicket,"My").start();
new Thread(buyTicket,"you").start();
new Thread(buyTicket,"yellow niu").start();
}
}
class BuyTicket implements Runnable {
private int ticketNums = 10;
boolean flag = true;
@Override
public void run() {
while (flag) {
try {
buy();//调用buy()方法
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
//synchronized同步方法
private synchronized void buy () throws InterruptedException {
if (ticketNums <= 0) {
flag = false;
return;
}
System.out.println(Thread.currentThread().getName() + "拿到" + ticketNums--);
}
}
线程等待(wait)
- 表示线程一直等待,直到其他线程通知,与slepp不同的是,wait()会释放锁
线程唤醒(notify)
- 作用:唤醒一个正在等待的线程
2.线程的优先级
-
优先级高的线程先分配处理器资源
-
每一个 Java 线程都有一个优先级,这样有助于操作系统确定线程的调度顺序。
-
Java 线程的优先级是一个整数,其取值范围是 1 - 10 ,默认是5。
-
优先级高的不一定先执行
package Thread;
public class PriorityTest{
public static void main(String[] args) {
MyPrior myPrior = new MyPrior();
//主线程默认是5
System.out.println(Thread.currentThread().getName()+"->"+Thread.currentThread().getPriority());
Thread t0 = new Thread(myPrior);
Thread t1 = new Thread(myPrior);
Thread t2 = new Thread(myPrior);
Thread t3 = new Thread(myPrior);
Thread t4 = new Thread(myPrior);
//先设置优先级,再启动
t0.setPriority(10);
t0.start();
t1.setPriority(9);
t1.start();
t2.setPriority(8);
t2.start();
t3.setPriority(3);
t3.start();
t4.setPriority(1);
t4.start();
}
static class MyPrior implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"->"+Thread.currentThread().getPriority());
}
}
}
3.创建线程的方法
Java 提供了三种创建线程的方法:
- 通过实现 Runnable 接口;
- 通过继承 Thread 类本身;
- 通过 Callable 和 Future 创建线程。(了解即可)
注:推荐使用实现Runnable接口的方法,避免了java单继承的局限性
继承Thread类
package Thread;
/**
*创建线程的方法1:继承Thread类,重写run()方法,调用strart()开启线程
* run()方法和Start()方法同时执行,交替执行
* 由cpu调度执行
*/
public class ThreadTest extends Thread{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("我在学习多线程"+i);
}
}
public static void main(String[] args) {
ThreadTest threadTest = new ThreadTest();
threadTest.start();
for (int i = 0; i < 100; i++) {
System.out.println("我在学习进程"+i);
}
}
}
实现Runanble接口
package Thread;
/**
* 创建线程方法2:继承Runnable接口,重写Run()方法,创建Runnable接口的实现类的对象,来调用start()开启线程
*/
public class RunnableTest implements Runnable{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("我在学习多线程"+i);
}
}
public static void main(String[] args) {
创建线程Thread对象,来调用start()方法,代理
ThreadTest tt = new ThreadTest();
tt.start();
for (int i = 0; i < 100; i++) {
System.out.println("我在学习进程"+i);
}
}
}
4.锁(Lock)
死锁:多个线程操作同一资源,各自抱着对方需要的资源
synchronized和Lock的区别
- Lock是显示锁,synchronized是隐式锁
- Lock只有代码锁,synchronized有代码锁和方法锁
- 使用Lock锁,性能更好
- 有优先使用顺序:Lock锁>同步代码块(已经进入了方法体,分配相应的资源>同步方法(在代码体之外)
package Thread;
//模拟买票,加锁
import java.util.concurrent.locks.ReentrantLock;
public class LockTest {
public static void main(String[] args) {
Lock2 lock2 = new Lock2();
new Thread(lock2,"a").start();
Thread t2 = new Thread(lock2);
t2.setPriority(10);
t2.start();
new Thread(lock2,"c").start();
}
}
class Lock2 implements Runnable{
//定义锁
private final ReentrantLock lock = new ReentrantLock();
int TicketNums = 10;
@Override
public void run() {
while(true){
try {
lock.lock();//加锁
if (TicketNums<0){
break;
}
else {
System.out.println(Thread.currentThread().getName()+"买到的票为"+TicketNums--);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
} finally {
lock.unlock();//解锁
}
}
}
}
5.Lambda表达式
作用:
-
避免匿名内部类定义过多
-
使代码更加简洁
本质:函数式接口编程
- 函数式接口定义:
- 任何一个接口,如果只包含唯一一个抽象方法,那就是一个函数式接口
- 对于函数式接口,我们可以通过Lambda表达式来创建接口的对象
package Thread;
/*
Lambda表达式
*/
public class LambdaTest {
//1.静态内部类
static class Like2 implements ILike{
@Override
public void lambda() {
System.out.println("i like lambda静态内部类");
}
}
public static void main(String[] args) {
//实现类
ILike like = new Like1();
like.lambda();
//实现静态内部类
like = new Like2();
like.lambda();
//局部内部类
class Like3 implements ILike{
@Override
public void lambda() {
System.out.println("i like lambda局部内部类");
}
}
like = new Like3();
like.lambda();
//匿名内部类,没有类的名称,必须借助接口或者父类
like = new ILike() {
@Override
public void lambda() {
System.out.println("i like lambda匿名内部类");
}
};
like.lambda();
//用lambda表达式
like = ()->{
System.out.println("i like lambda表达式");
};
like.lambda();
}
}
//定义一个函数式接口
interface ILike{
void lambda();
}
//实现类
class Like1 implements ILike{
@Override
public void lambda() {
System.out.println("i like lambda");
}
}
6.JUC并发安全集合
CopyOnWriteArrayList是安全的集合
package Thread;
import java.util.concurrent.CopyOnWriteArrayList;
//JUC并发安全的集合
public class JUCTest {
public static void main(String[] args) {
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
for (int i = 0; i < 1000; i++) {
new Thread(()->{//lamda表达式
list.add(Thread.currentThread().getName());
}).start();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(list.size());
}
}
7.线程池
package Thread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class PoolTest {
public static void main(String[] args) {
//创建服务,创建线程池
//newFixedThreadPool的参数是线程池的大小
ExecutorService service = Executors.newFixedThreadPool(10);
//执行
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
//关闭
service.shutdown();
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}