前言
Java的JDK的安装是直接用的IDEA自带的那个安装的,
然后学习的教程也是廖雪峰老师的教程。
Java教程 - 廖雪峰的官方网站 (liaoxuefeng.com)
Java常见三种版本:
Java SE:Standard Edition(标准版)
Java EE:Enterprise Edition(企业版,web开发)Java ME:Micro Edition(移动端,如半智能手机)
三者的关系图
┌───────────────────────────┐
│Java EE │
│ ┌────────────────────┐ │
│ │Java SE │ │
│ │ ┌─────────────┐ │ │
│ │ │ Java ME │ │ │
│ │ └─────────────┘ │ │
│ └────────────────────┘ │
└───────────────────────────┘
JDK:Java Development Kit
JRE:Java Runtime Environment(Java虚拟机)
设置环境变量
JAVA_HOME 指向JDK的安装路径 比如:C:\Users\DELL\.jdks\openjdk-17.0.2
然后在PATH里面添加bin:%JAVA_HOME%\bin
添加成功后,就可以在cmd里面尝试一下了:
C:\Users\DELL>java -version openjdk version "17.0.2" 2022-01-18 OpenJDK Runtime Environment (build 17.0.2+8-86) OpenJDK 64-Bit Server VM (build 17.0.2+8-86, mixed mode, sharing)
Java的bin目录下也有很多东西,比如java就是JVM虚拟机,javac是Java的编译器,jar可以把class打包成jar,javadoc可以从Java源码中自动提取注释并生成文档,jdb可以用于开发阶段的调试。
第一个程序
hello world
public class Hello { public static void main(String[] args) { System.out.println("Hello World!"); } }↑文件名必须与类名相同,即Hello.java
然后编译为class文件,之后就可以运行了。
一个java源码只能定义一个public 类型的class,且要与文件名一致。
PS F:\Java> javac .\Hello.java PS F:\Java> java Hello Hello World!
执行java虚拟机运行时,传入的是类名,而不是.class文件。Java虚拟机会自动寻找这个类名的class文件并执行。
Java 11 新增功能,虚拟机可以直接运行.java文件
PS F:\Java> java Hello.java Hello World!
Java规定,某个类定义的public static void main(String[] args)是Java程序的固定入口方法,Java程序总是从main方法开始执行。
命名规则
类名:首字母大写,驼峰命名法。
方法名:首字母小写,驼峰命名法。
注释
// 单行注释
/* */ 多行注释
/** */ 文档注释(如果有多行,通常每行以*开头)
生成文档的命令:javadoc xxx.java
javadoc -d doc -encoding utf-8 -charset UTF-8 HelloWorld.java-d指定路径,encoding指定编码 charset指定字符集,这样可以防止中文乱码的问题。
public class HelloWorld { /** * 这是第一个java程序 * @param args 喵 */ public static void main(String[] args) { System.out.println("HelloWorld"); // 单行注释
/* 多行注释 */ } }
变量
Java中有两种变量类型,基本类型的变量和引用类型的变量。
变量必须先定义后使用。定义时可以赋初值。与C语言类似。
基本数据类型
整数类型:byte、short、int、long
浮点类型:float、double
字符类型:char
布尔类型:boolean
Java只定义了有符号的整型,最大范围如下:
byte:-128 ~ 127
short: -32768 ~ 32767
int: -2147483648 ~ 2147483647
long: -9223372036854775808 ~ 9223372036854775807
float:3.4x1038
double:1.79x10308
boolean:true和false
char:ASCII码或者Unicode表示的单个字符,用单引号引起来。
Java中数字也可也加下划线方便人看,不影响数值。
byte a1 = 56; short a2 = -1256; int a3 = 114_514; // 可以加下划线方便观看 long a4 = 1_919_810L; // long类型需要在后面加L int a5 = 0b10010; // 二进制以0b开头 int a6 = 0x000000b; // 十六进制以0x开头 int a7 = 0554; // 八进制以0开头 float f1 = 3.14f; // float后面加f float f2 = 3.14e32f; // 科学计数法表示3.14乘10的32次方 double d1 = 1.79e308; double d2 = -1.79e308; double d3 = 4.9e-324; boolean b1 = true; boolean b2 = 5 > 2; int age = 12; boolean b3 = age>15; boolean bf = true; char c1 = 'A'; // 可以表示显示常规的ASCII字符,用单引号引起来(双引号是字符串类型,要区分) char c2 = '中'; // 也可以表示unicode字符 System.out.println(c1); System.out.println(c2);
上述是基本类型,其他类型都是引用类型。
引用类型类似于C语言的指针。
String:字符串,用双引号引起来。
String s1 = "这是一句话。"; System.out.println(s1);
常量
常量:在定义时加上final修饰符,就是定义常量。
final double PI = 3.1415926; //常量定义后不可修改
var 自动推断变量类型
StringBuilder s2 = new StringBuilder(); var s3 = new StringBuilder(); // 这样写的效果和上面相同。编译器可以自动推断简单变量。
变量作用域:
定义变量时要使用最小作用域原则,而且尽量不要重名。
数值运算
Java的数值运算基本与数学一致。
加+ 减- 乘* 除/ 括号() 除余%
简写运算符+= -= *= /=
++自增 --自减
整数相除永远会是整数。
除以0时编译不会报错,运行会报错。
计算超出范围不会报错,会导致溢出。
位运算
移位运算>> << 先转换成二进制再运算。首位表示符号。
无符号的右移>>>
int a = 3; // 00000000 00000000 00000000 00000011 int n1= a<<1; // 00000000 00000000 00000000 00000110 // 首位表示符号0正1负 // 负数向右移位首位不会变 int n2 = -536870912; int n3 = n2 >> 1; // 11110000 00000000 00000000 00000000 = -268435456 int n4 = n2 >> 2; // 11111000 00000000 00000000 00000000 = -134217728 int n5 = n2 >> 28; // 11111111 11111111 11111111 11111110 = -2 int n6 = n2 >> 29; // 11111111 11111111 11111111 11111111 = -1 // 无符号的向右移位>>> 最高位移动之后会补0
对于bytes和short类型的数据移位,会先转换成int再移位。
与& 或| 非~ 异或^
Java运算的优先级:
()
! ~ ++ --
* / %
+ -
<< >> >>>
&
|
+= -= *= /=
记不住用括号。
类型转换
自动类型转换:运算时总是转换为范围更大的类型。
强制类型转换:前面加括号,括起来类型。
强制类型转换时,高位会被直接舍弃,得到错误结果。
short i2 = (short)a3;
浮点数运算由于二进制转换的问题常常不准确,会产生误差。
比较浮点数是否相等通常采用做差然后判断是否小于一个很小的数。
整数和浮点数运算时会自动转换为浮点数。
浮点数除以0之后不会报错,会返回特殊数值。
double fl1 = 0.0/0; // NaN 不是数字 double fl2 = 1.0/0; // Infinity 无穷 double fl3 = -1.0/0; //-Infinity 负无穷
强制转换成整数时会丢弃小数部分。
如果要四舍五入可以先加上0.5再进行转换。
当整数不够时会转换为整数能表示的最大数值。
布尔运算
比较运算符:>,>=,<,<=,==,!=
与运算 &&
或运算 ||
非运算 !
短路运算
三元运算符 x?y:z 原理和c语言一致。
Java中字符都使用Unicode编码,将字符赋值给int即可查看对应的编码。
可以使用\uXXXX 十六进制 表示对应编码的字符
Java中可以用+符号连接任意字符串和其他数据类型,其他类型都会被转换成字符串。
Java13开始可以用三个双引号来引起来多行字符串(和Python类似)""" """
多行字符串前面共同的空格会被去掉,总是以最短的行首空格为基准。
字符串不可变,只是变了指针。
字符串可以赋值为null,即不指向任何字符串。和空字符串不一样。
数组类型
类型[] 变量名 这是Java的写法。当然也可以用C的写法 类型 变量名[]
比如int[] a = new int[5] 或者 int a[] = new int[5]
创建初始化时必须写new表示创建一个数组。
Java数组初始化时都有默认值,比如整型默认是0,浮点型0.0,布尔型false
数组一旦创建,长度就不可变。
可以用数组.lenth获取数组大小
int[] a = new int[5]; a[0] = 1; a[1] = 2; System.out.println("" + a[0] + a[1] + a[2]); // 120 System.out.println(a.length); // 5
也可以在初始化时直接写好值,不写长度,会自动推断lenth。
int[] b = new int[] {1,5,8,6,9,7,4,7}; System.out.println(b.length); // 8
甚至后面的new int[] 都可以省略,更加简化。
字符串数组
String[] names = { "喵", "旺" }; System.out.print(names[0]); System.out.print(names[1]);
输出
println是printline的缩写,打印后会在末位打印一个换行符。
直接print是没有换行符的。
printf是格式化输出,可以把后面的内容转换成制定形式,于C语言类似。
%d 整数 %x 十六进制整数 %f 浮点数 %e 科学计数法的浮点数 %s 字符串
%% 转义表示%本身
输入
scanner方法:
导包import java.util.Scanner;
Java中的import必须放在源码的开头
Scanner scanner = new Scanner(System.in); String name = scanner.nextLine(); int age2 = scanner.nextInt();
创建Scanner对象,放入标准输入流,然后读入一行文本。
nextInt()可以自动把读入的文本转换为对应的数据类型,而不需要手动转换。
if判断
if(条件1){
//条件1满足执行
} else if (条件2) {
//条件1不满足且条件2满足时执行
} else {
//都不满足时执行
}
if语句块只有一行语句时,可以省略花括号。(但是不推荐省略花括号)
浮点数判断通常会有误差所以不靠谱,通常采用差值小于临界值来判断。Math.abs(x - 0.1) < 0.00001
Java中判断值类型的相等可以直接用==,但是引用类型不可以,就算值相同,==的结果也是false。
因为引用类型通常表示为不同的对象。此时应该用equals方法进行比较。
String test1 = "Hello"; String test2 = "Hell"; test2 = test2 + "o"; System.out.println(test1==test2); // ==>false System.out.println(test1.equals(test2)); // ==>true
用equals方法时前面那个变量(比如此处的test1)一定不能是Null,否则会报错。
可以用短路运算符test1 != Null && test1.equals(test2),也可以把前者写成无论如何都不会是Null的变量。
Switch语句:
和C语言基本一致。
没有花括号,case穿透,default等特性也被保留。
匹配字符串时是保证内容相等。
Java12新特性:类似模式匹配。
用新语法可以不写break也保证一一对应。
switch (test1){ case "hello" -> System.out.println("hello"); case "miao", "wang" -> { System.out.println("miao"); // 花括号可以写多条语句 System.out.println("wang"); } default -> System.out.println("23333"); }还可以用新特性直接赋值。
int num2333 = switch (test1){ case "hello" -> 1; case "miao" -> 2; default -> { System.out.println("miao"); yield 0; // 有多条语句时用yield返回返回值 } };
while循环和do while循环与C语言一致。
和C语言的for循环基本一致。
计数器i要尽量定义在for循环的括号内部,变量最小化原则。
for each循环
和python的for xxx in xxx比较相似
for (String sss1:names){ System.out.println(sss1); }有时候前面的变量类型也可以换做var(更像python的for循环了
continue和break语句也和C语言一致。
打印数组:
使用循环遍历打印。
直接打印是JVM中的引用地址。
也可以用现有的方法Arrays.toString(数组)
Arrays.sort(数组)排序 默认从小到大 数组里未定义的默认为0 方法直接对数组进行操作,不是返回值。
二维数组
除了定义的时候不像C语言那样必须写长度,其他基本一致
转换字符串Arrays.deepToString(数组)
命令行参数
是String数组,会把运行命令行时附加的参数以空格隔开放到一个字符串数组里面。
Java面向对象
一个Java源文件只能定义一个Public类
class Person{ public String name; public int age; } ... Person p1 = new Person(); p1.name = "红"; p1.age = 18; System.out.printf("%d, %s",p1.age,p1.name);
if(string == null | string.isBlank()) 判断字符串是否为null或者为空
this 没有命名冲突时可以省略this,与C++基本一致。
方法参数:必须按照定义严格依次填写。
可变参数:数组,用类型...定义,比如String...
可变参数可以保证调用方无法传入null
构造方法:基本与C++一致。
没有构造方法时使用默认的构造方法。
想要两种不同参数的构造方法,就同时写两个构造(类似于C++的重载)
一个构造方法可以调用其他构造方法,便于代码复用。 this(string);
默认构造方法构造出来的默认值,引用类型是null,整型是0,布尔型是false
构造方法可以重载,里面写的其他方法也可以重载。
Java可以用extends关键字来继承父类
class Student extends Person{}
继承后默认就有了父类的所有字段,所以不能定义和父类同名的字段。
没写extends的时候相当于自动加上了extends Object。
直接继承无法访问父类的private方法或者字段,可以改为protected
子类引用父类字段时,可以写super.xxx
构造子类时,如果没写父类的构造方法,相当于默认加一个super()
当父类没有这种无参的构造方法时就会报错。
所以可以在子类构造时手动调用父类的构造方法super(name, age)
阻止继承
java15新增sealed修饰class,可以写明那些类可以继承这个类。
public sealed class Person permits Student, Teacher{}
这样就是只允许Student类和Teacher类继承Person。继承时要在前面用final、non-sealed或者sealed修饰class。
向上转型
子类安全的变为父类
Person p1 = new Student();
向下转型
Person p1 = new Student();
Student s1 = (Student) p1;
向下转型可能会失败,报错ClassCastException
instanceof 判断是否是制定类型或其子类 if(p1 instance of Person) null永远是false
java14开始可以判断之后直接转换
if(p1 instance of Student s){可以直接使用变量s}
组合与继承的区分
Override重写,子类和父类相同的方法名、返回值,相同的参数。
Overload重载,子类和父类相同的方法名、返回值,不同的参数。
可以在方法前加上@Override或者@Overload来让编译器帮你检查是否写错了。
多态:动态调用方法。
重写Object方法
Srting toString()
boolean equals(Object o)
hashCode()
final修饰的方法不能被重写
final修饰的类不能被继承
final修饰的字段不能被修改
抽象方法:不需要具体实现
public abstract void run();
抽象方法必须写在抽象类中
abstract class Person {}
抽象类无法被实例化
面向抽象编程
接口
如果抽象类没有字段,全都是抽象方法,就可以把抽象类写为interface接口
接口比抽象类还抽象,不能有字段,所有方法都是public abstract(因此可以省略)
用类来实现接口时,不是继承extends,而是implements实现。
一个类只能从另一个类继承,但是可以写多个接口的实现。用逗号隔开。
接口继承接口使用extends
default方法,需要写实现。可以调用抽象类里面的方法,这样只要复写抽象类的方法就能保证default方法不变。
静态static
静态字段:类里面共享的字段。静态字段不属于实例。推荐使用类名.静态字段来访问。
静态方法:不需要实例变量,通过类名即可调用。
接口可以有静态字段,必须用final修饰:public static final int NUM = 1;
因为接口中静态字段默认都是public static final,所以可以省略,直接写int效果是一样的。
包package
第一行声明包名package com.company;
包名和文件路径有对应关系。
包没有父子关系,java.util和java.util.zip是两个不同的包。
包作用域:不用public、protected或者privata修饰的方法就是包的作用域,仅限同一个包里访问。
导包
①写出完整类名调用。
②import包然后写简单类名。
import static 导入所有静态类和静态方法
简单类名查找顺序:包里面、import导入的、java.lang里面的
如果两个包里面有类名冲突,则只能导其中的一个
写包时通常采用倒置域名的方式确保唯一性。
嵌套类
内部类Inner Class:定义在一个class内部的class
不能单独存在,依附于外部类Outer Class
Outer outer = new Outer("Nested"); // 实例化一个Outer Outer.Inner inner = outer.new Inner(); // 实例化一个Inner要实例化内部类,必须先创建外部类,然后调用外部实例的new方法来实例化内部类
内部类可以访问外部类的private属性和方法
静态内部类:static修饰
不再依附于外部类实例,是一个完全独立的类,但是可以访问外部类的静态字段和静态方法。
匿名类:Anonymous class
在方法内部通过匿名类定义。
接口不能实例化,但是可以实现接口的匿名类,通过new来实例化匿名类
jar包:本质是ZIP,处理类之间的关系。
模块jmod,处理jar包之间的依赖关系。
字符串比较相等用equals()
忽略大小写equalsIgnoreCase()
是否包含子串contains()
序号indexOf()
倒序lastIndexOf()
开头startsWith()
结尾endsWith()
切片substring(2) 类似python的str[2:]
substring(2, 4) 类似python的str[2:4]
去除首尾空白字符(\r\n\t等)trim
strip,去除首尾空白字符,包括中文空格\u3000
stripLeading()去首 srtipTeailing() 去尾
isEmpty空字符(长度为0)isBlank()只包含空白字符
如果为null则会引发异常。
替换字符串
.replace(a,b) replace替换
replaceAll(正则.b) 正则替换
分割字符串
分割成字符串数组,传入正则表达式
String[] ss = s.split("\\,")
拼接字符串:用指定的字符串连接字符串数组
String s = String.join(", ", strArry) // 逗号分隔
格式化字符串
格式化字符串.formatted(参数列表)
或者静态方法String.format("格式化字符串", 参数列表)
转化为字符串
String s = String.valueOf()
getInteger是把系统变量转换为数字
Integer.getInteger("java.version"); // 版本号,11
要把字符串转换为数字
Integer.parseInt("ff", 16) // 以16进制载入
char[] cs ="miao".toCahrArray() 转换为char[]
String s = new String(cs) 转换为字符串
Java字符串可以直接相加,但是每次相加都是生成新字符串,效率较低。
可以使用StringBuilder
StringBuilder sb = new StringBuilder(1024);
sb.append(","); sb.append("miao");
String s = sb.toString();
也可以链式操作sb.append().append().append().insert(0, "Hello")
定义中返回this就可以实现这种链式操作。
StringJoiner()字符串列表的字符串拼接。
var sj = new StringJoiner(",", "开头","结尾")
sj.add("要加的内容")
sj.toString(); 转换为字符串。
还有String.join()的静态方法
包装类
可以把基本类型视为对象(引用类型)
只包含一个实例字段。
java核心库自带包装类型
java.lang.Boolean
java.lang.Byte
java.lang.Short
java.lang.Integer
java.lang.Long
java.lang.Float
java.lang.Double
java.lang.Character
通过静态方法valueOf()创建Integer实例(另一个parseInt()返回的是int型)
所有包装类都是不变类,作为引用类型不能用==比较,而是用equals比较。
枚举类
可以用enum标记
enum Weekday { SUN, MON, TUE, WED, THU, FRI, SAT}
enum Weekday {SUN(7), MON(1), ....}
这样写括号的话就相当于调用自己类的构造函数,括号里面是参数
可以自己定义一个public变量,初始化的时候构造函数赋值
public final int dayValue;
private Weekday(int dayValue){this.dayValue=dayValue;}
枚举类可以用equals,也可以直接用==来比较相等!
Weekday.SUN.name(); // "SUN"
Weekday.SUN.ordinal(); // 1
record类
java14引入
public record Point(int x, int y){}
这个类除了用final修饰每个字段外,还自动构建了toString euqals hashCode等方法。
如果构造时要检查值对不对,可以自己编写构建,但是不需要自己赋值,编译器会自己加上。
也可以自己添加静态方法。
BigInteger 可以定义非常大的数
做运算时只能用实例方法。
BigDecimal任意大小精度完全准确的浮点数。
比较必须用compareTo()而不用equals(),因为equals会同时比较精度信息。
Math.abs();绝对值
Math.max();Math.min(); 比较两个数中最大的和最小的
Math.pow(x,y); 计算x的y次方
Math.sqrt(); 开根号
Math.exp(); e的x次方
Math.log() 以e为底的对数
Math.log10() 以10为底的对数
sin cos tan asin acos 三角函数
Math.PI π MAth.E e
Math.random() 随机生成一个大于等于0小于1的数。
异常处理
try { String s = processFile(“C:\\test.txt”); // ok: } catch (FileNotFoundException e) { // file not found: } catch (SecurityException e) { // no read permission: } catch (IOException e) { // io error: } catch (Exception e) { // other error: }错误(Error)是严重错误,如内存耗尽(OutOfMemoryError)、栈溢出等,程序一般无能为力。
Exception是运行时错误,可以被捕获和处理。
分为RuntimeException和其他(IOException、ReflectiveOperationException等)
除RuntimeException及其子类,都是必须捕获的异常。
不需要捕获的异常包括Error及其子类,RuntimeExcep及其子类
不想写try,可以把main()后面加上throws Exception
e.printStackTrace() 打印异常栈
抛出异常
throw new IllegalArgumentException(e)
可以捕获并传递上一个异常e,这样可以获取完整的异常信息。
常用异常:
Exception
│
├─ RuntimeException
│ │
│ ├─ NullPointerException
│ │
│ ├─ IndexOutOfBoundsException
│ │
│ ├─ SecurityException
│ │
│ └─ IllegalArgumentException
│ │
│ └─ NumberFormatException
│
├─ IOException
│ │
│ ├─ UnsupportedCharsetException
│ │
│ ├─ FileNotFoundException
│ │
│ └─ SocketException
│
├─ ParseException
│
├─ GeneralSecurityException
│
├─ SQLException
│
└─ TimeoutException
IO InputStream OutputStream
File对象
既可以表示文件,也可以表示路径。
File f = new File("路径")
f.getPath() f.getAbsolutePath() f.getCanonicalPath() 传入的路径 绝对路径 规范路径
File.separator 根据操作系统平台打印/或者\
isFile()
isDirectory()
canRead canWrite canExecute length文件字节大小
createNewFile()创建新文件 file.delete()删除文件(如果是路径需要保证路径为空才能删除)
File f = File.createTempFile("");
f.deleteOnExit();
创建临时文件,JVM退出时自动删除
mkdir() 创建路径 mkdirs() 套娃创建路径
list() listFiles() 列出目录的文件和子目录名
listFiles可以重载accept方法来筛选文件。
InputStream基本输入流
用close()方法来关闭流。
异常处理可以写finally里面,也可以用try(InputStream dd = new FileInputStream("文件名")){}这样编译器可以自动关闭流。
定义缓冲区 byte[] buffer = new byte[1000];
read(buffer)方法读取数据时会造成阻塞,等这一天执行完才执行下一条语句。
FileInputStream
OutputStream基本输出流
write()
close()方法关闭
flush()强制写入缓冲区
FileOutputStream
Reader 输入流接口(字符流)
Reader reader = new FileReader("src/readme.txt", StandardCharsets.UTF_8); //指定编码
try (Reader reader = new InputStreamReader(new FileInputStream("src/readme.txt"), "UTF-8")) {
// TODO:
}
Writer 输出流
集合list
public class Main { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("apple"); // size=1 list.add(null); // size=2 list.add("pear"); // size=3 String second = list.get(1); // null System.out.println(second); } }
for (Iterator<String> it = list.iterator(); it.hasNext(); ) { String s = it.next(); System.out.println(s);
hashMap
Map<String, Student> map = new HashMap<>(); map.put("Xiao Ming", s); // 将"Xiao Ming"和Student实例映射并关联 Student target = map.get("Xiao Ming"); // 通过key查找并返回映射的Student实例
本文地址:https://blog.jixiaob.cn/?post=79
版权声明:若无注明,本文皆为“赵苦瓜のBlog~”原创,转载请保留文章出处。