Java 基础核心学习笔记,参考书籍《Java核心技术 卷I》
Java
基础核心学习笔记
文章目录
- `Java` 基础核心学习笔记
1、基础语法
1.1、八大数据类型
byte(2字节)、short(2字节)、int(4字节)、long(8字节)、float(4字节)、double(8字节)、char(2字节)、boolean(1位,至少需要一个字节)
在Java中,所有的数值类型占据的字节数与平台无关。
整型数据类型:byte、short、int、long
在Java中没有 unsigned
形式来表示一个一个整数类型,如果需要表示一个不可能为负数的的整数值,可以使用对应数据类型对应的类,使用toUnsignedInt()
方法,来处理这个数,然后在装换成原来的数据类型。
public static void main(String[] args) { byte b = -127; System.out.println(b); // 输出-1 int i = Byte.toUnsignedInt(b); // 装换得到一个 0-255的数值 // 转换方式:如果是真值,就是本身,如果是负数就是 2最大真值-|原值| System.out.println(i); // 输出255}
浮点类型:float(单精度,有效位6~7)、double(双精度,有效位是双精度的两倍)
三个特殊值:Double.POSITIVE_INFINITY
(正无穷大)、Double.NEGATIVE_INFINITY
(负无穷大)、NaN
(不是一个数字)
public static final double POSITIVE_INFINITY = 1.0 / 0.0;public static final double NEGATIVE_INFINITY = -1.0 / 0.0;public static final double NaN = 0.0d / 0.0;
double a = Double.POSITIVE_INFINITY;double c = Double.NEGATIVE_INFINITY;double naN = Double.NaN;System.out.println(naN); // 输出 NaNSystem.out.println(a); // 输出InfinitySystem.out.println(c); // 输出-Infinity
如何判断一个浮点数是否为一个数值?Double、Float提供了 isNaN()
来判断一个浮点数是否为一个数值。上面这些内容对于Float也适用。
char类型:表示单个字符
char类型的值可以表示为十六进制,范围是 \u0000~\uFFFF
,例如 \u03c0
表示字符 π
。
System.out.println('\u03c0'); // 输出 π
注意:
- 注释中的
\u
也可以解析为字符。
// \u000A 你好System.out.println('\u03c0'); // 输出 π// 上面代码会报错,因为 \u000A会解析成一个换行符,导致 你好 会换行导致报错
\u
后面必须跟一个十六进制数。
// c:\usersSystem.out.println('\u03c0'); // 输出 π// 上面也会报错,\u后面跟的是ers,不是一个十六进制数
1.2、变量与常量
变量:初始化、赋值、标识符的命名规则等
千万不要使用未初始化的局部变量,如果局部变量没有初始化直接使用就会报错。但是类的成员变量,在类构造时会初始化属性值。
在Java10
中,对于局部变量,可以通过变量的初始值来判断变量的类型,可以使用 var
关键字声明变量就无须指定类型。
var a = 10;var str = "Hello,World!";
常量:使用
final
关键字来修饰,表示这是个常量,常量只能被赋值一次,一旦赋值就不能改变,常量名通过全部大写。
1.3、运算符
算数运算符:+、-、*、/、%(取模)
注意:整数除以0时会报出异常,而浮点数除以0会得到无穷大。
数学函数 Math
// 两个常量 PI和ESystem.out.println(Math.PI);System.out.println(Math.E);// 常用方法public static double abs(double num); // 获取绝对值,有多种重载。public static double ceil(double num); // 向下取整。public static double floor(double num); // 向上取整。public static long round(double num); // 四舍五入。public static double sqrt(double x); // 求算数平方根public static double pow(double x,double y); // 求x的y次方public static int floorMode(int x,int y); // 对于负数求余数,x被除数,y除数,但是如果y是负数也会得到一个负数余数
数据类型转换和强制类型转换
从短字节转换到高字节,可以自动转换,但是整数转换成浮点数类型时可能会发生精度的缺失,浮点数转换成整数也会去除小数。如果从字节转换成低字节,需要进行强制类型转换,这样可能得到错误的结果,强制转换成另一低字节类型时,超出了目标类型的表示范围,就会得到一个错误的数。
二元运算符:+=、++(自增)、–(自减)等
- +=:例如:x+=3 ,表示 x=x+3,对于其他的数学运算符也适用。
- ++:自增1,x++,表示x=x+1,自减类似。
int a = 2;System.out.println(a++); // 输出2,a=3System.out.println(++a); // 输出4,a=4// 上面的a++是先赋值后加1,a++这个表达式还是原来的值,但是a后面加1了。// ++a是先加1后赋值,++a这个表达式的值是a加1后的结果。// 对于 -- 一样// 由于运算符是改变变量的值,所以 4++ 这是一个不合法的语句
三元运算符:
条件 ? 表达式1 : 表达式2
,条件成立输出为表达式1,不成立输出为表达式2。
System.out.println(1>2 ? "1大于2" : "2大于1"); // 输出2大于1
位运算符:&(与)、|(或)、^(异或)、~(非)、>>(右移)、<>>(无符号右移,高位使用0补位)。位运算是按照二进制的位进行运算。
1.4、字符串
字符串使用 String
这个预定义类来表示,它是一个对象,不属于基本数据类型。
字符串拼接
在Java中,任何一个Java对象都可以转换成字符串,当一个字符串与一个非字符串进行拼接时,后者
会转换成字符串。
System.out.println(1+"1你好"); // 输出 11你好System.out.println(1+1+"你好"); // 输出 2你好
字符串不可变
String
没有提供任何修改字符串的方法,字符串一旦定义,就不能改变了。如果需要修改字符串,可以通过截取子串,然后通过拼接构建一个新的字符串。
String str = "Hello,World";str = str.substring(0,3); // substring()方法截取子串// 这个方法并不是修改了str,而是截取了一个新的子串,然后重新赋值给了str,"Hello,World!"这个字符串依然存在,后面会被GC回收掉
检测字符串相等
可以使用 equals()
方法区分大小写来检测两个字符串是否相等,equalsIgnoreCase()
不区分大小写判断字符串是否相等,相等返回true
,不相等返回false
。
System.out.println("Hello".equals("Hello")); // 输出trueSystem.out.println("test".equalsIgnoreCase("TesT")); // 输出true
注意:在Java中,不能使用 ==
来判断两个字符串是否相等, ==
只能判断字符串的位置是否在同一个位置上,同一位置的字符串必然是相等的,但是有相同的字符串放在不同的位置上。在Java中,字面量的字符串是共享的。
String s1 = new String("Hello,World!");System.out.println(s1=="Hello,World!"); // 输出false
空串与null
空串:指一个String对象的长度为0,可以通过 str.length()==0
或 "".equals(str)
来判断。
null串:指这个String对象都不存在,没有任何Java对象与变量有关联。
注意:如果检查字符串是否为空串,需要先检查是否为 null
串。
if(str!=null && str.length()!=0) // 先检查null
字符串
String
常用方法
int comparerTo(String other); // 如果字符串位于other之前返回一个负数,之后返回一个正数,相等返回0boolean isEmpty(); // 判断字符串长度是否为0,不能判断nullboolean startWith(String pre); // 判断字符串是否以pre开头boolean endWith(String suffix);String replace(CharSequence oldStr, CharSequence newStr); // 使用newStr替换字符串中所有的oldStr,CharSequence可以是String和StringBuilderString substring(int begin,int end); // 截取字符串的子串,从下标begin开始截取,end-1这个下标结束,截取的长度end-beginString toLowerCase(); // 将字符串的所有大写字母转换成小写String toUpperCase(); // 将字符串所有小写字母转换成大写String trim(); // 去掉字符串的头空格和尾空格static String join(CharSequence s,CharSequence... e); // e是可变参数,将e这些参数,通过s这个分界符连成一个新字符串,可变参数也可以是一个数组的重载方法String repeat(int count); // 返回字符串重复count次的新字符串,java11
CharSequence
是一种接口类型,所有字符串都属于这个接口。
构建字符串
需要多个简单的字符串拼接成一个字符串时,通过字符串的拼接这样浪费空间、时间,这时就可以使用StringBulider
类来构建字符串。
StringBuilder builder = new StringBuilder();builder.append("Hello");builder.append(",World!");System.out.println(builder.toString());
下面是StringBuilder
类常用方法:
int length(); // 返回字符串构造器中字符串的长度StringBuilder append(参数); // 将参数追加的构造器中,参数可以是字符串、基本数据类型除byte,也可以是char数组,也可以是一个StringBuilderStringBuilder insert(int index,参数); // 将参数插入到index下标的位置,参数和上面一样StringBuilder delete(int begin,int end); // 删除构造器中从begin到end-1这段字符串String toString(); // 返回构造器内容相同的字符串String reverse(); // 将构造器中的字符逆序
1.5、输入和输出
输入:在Java中,有很多输入方式,从控制台输入、文件输入等,下面介绍从控制台输入,文件输入在后面I/O流。
方式一:通过Scanner
类从控制台读取数据。
Scanner in = new Scanner(System.in);String s1 = in.next(); // 从控制台读取一串不包含空格、回车、换行的字符串String s2 = in.nextLine(); // 读取一行包含空格的字符串int x = in.nextInteger(); // 也可以读取基本数据类型int,double,float,long等
方式二:
通过BufferedReader
类从控制台读取数据。
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));System.out.println(reader.readLine()); // 读取一行包含空格字符串,会报异常,捕获就好,后面说异常System.out.println(reader.read()); // 读取单个字符,返回的是ASCII码
输出:格式化输出,按照需要的格式输出内容
Java5
中沿用C语言中输出函数printf()
,可以使用这个函数来格式化输出。
double x = 3.926;System.out.printf("%.2f",x); // 保留两位小数输出,自动四舍五入// % 表示使用对应参数来替换,f表示转换符,下面是其他类型的转换符
转换符 | 类型 |
---|---|
d | 十进制整型 |
x | 十六进制整型 |
o | 八进制整型 |
e | 指数浮点类型 |
a | 十六进制浮点类型 |
s | 字符串 |
c | 字符 |
b | 布尔 |
f | 浮点类型 |
也可以使用String
类中静态方法format()
方法来格式化一个字符串,返回值是一个新字符串。
String result = String.format("x = %.2f",x);System.out.println(result);
下面是对日期的格式化:
System.out.printf("日期为 ==》 %tF",new Date()); // 输出 日期为 ==》 2022-04-01// 格式是已%t开头,下面表中任意一个字符结尾
1.6、流程控制
快级代码、条件语句(if、switch)、循环语句(while、for、do…while)、顺序语句。
条件语句
String type = "A";// 会使用type与下面的case标签进行匹配,匹配成功就执行,不成功就匹配下一个,所有没匹配就执行default// type只能是byte、short、char、int、枚举、String// case的类型可以是基本数据类型常量、字符串常量,不能是变量// 遇到break就跳出,没有break就继续匹配下一个switch (type){ case "A": System.out.println("a"); break; case "B": System.out.println("b"); break; default: System.out.println("错误");}
循环语句
在循环语句中,如果使用浮点数作为结束条件的话,需要小心浮点数精度问题,有可能进入死循环。注意使用break(直接跳出循环)和continue(填过这次循环,进入下一次循环)
1.7、大数
如果基本数据类型精度不能满足要求,可以使用BigInteger
(整数)和BigDecimal
(浮点数),这两个类可以运算任意长度的数值。
BigInteger
BigInteger bigInteger = BigInteger.valueOf(1000); // 将普通数值装换成bigIntegerSystem.out.println(bigInteger);BigInteger bigInteger1 = new BigInteger("10000000000000"); // 将一个字符串转换十进制数值System.out.println(bigInteger1);
注意:在Java中,对于大数没有四则运算符,不能使用+、-等。只能通过方法来实现。
BigInteger add(BigInteger other); // 两个大数相加,返回一个新大数BigInteger subtract(BigInteger other); // 减BigInteger multiply(BigInteger other); // 乘BigInteger divide(BigInteger other); // 除BigInteger mod(BigInteger other); // 取余BigInteger sqrt(BigInteger other); // 开平方根BigInteger compareTo(BigInteger other); // 比较,小于other返回负数,相等返回0,大于返回正数
BigDecimal
这个类和BigInteger
类似,不详细介绍。
1.8、数组
数组声明、数组初始化,数组一旦声明了就不能改变它的长度,但是可以修改它的单个元素。在Java中允许长度为0的数组,长度为0不代表这个数组为null
。初始化化数组但是未赋值,int的数组的初始值为0,boolean的初始化为false,对象数组的初始化为null
。
for each循环
for(x : collection) {}
它定义一个变量来暂存数组和集合中的每一个元素,collection必须是数组或者是实现了Iterable
接口的类对象。但是这种方式没有通过下标来获取数组元素,后面需要下标很麻烦。
数组拷贝
在Java中,允许将一个数组变量赋值给另一个数组变量,这两个数组变量就指向同一个数组,一个数组改变,另一个数组也会发生改变。如果需要拷贝一个数组,但是元数组不发生改变,就需要Arrays.copyOf()
,进行数组拷贝。
int[] array = new int[]{1,2,3,4};// 参数1:拷贝的数组,参数2:新数组的长度;如果新数组长度小于元素组长度,只拷贝前面的,如果大于,其余的元素是默认值int[] newArray = Arrays.copyOf(array,array.length()*2); // 返回一个新数组,与元数组没有任何关系
数组排序
可以使用Arrays.sort()
对一个数组进行排序,从小到大。sort()方法使用的是快速排序法,可以所有数据类型进行排序,包括对象,只需要创建一个Comparator
类来定义自己的排序规则即可。
例如下面:
package com.tanke.jichu;import java.util.Arrays;import java.util.Comparator;/** * @author tanKe * Date on 2022/4/1 15:58 */public class Test { public static void main(String[] args) { Student[] students = new Student[3]; students[0] = new Student("001","172","135"); students[1] = new Student("002","173","135"); students[2] = new Student("003","171","135"); // 按照身高来排序 Arrays.sort(students, new Comparator<Student>() { @Override public int compare(Student o1, Student o2) { if (Double.parseDouble(o1.getHeight())>Double.parseDouble(o2.getHeight())){ return -1; }else if (Double.parseDouble(o1.getHeight())<Double.parseDouble(o2.getHeight())){ return 1; } return 0; } }); for (Student student : students) { System.out.println(student); } //Student{id='002', height='173', weight='135'}//Student{id='001', height='172', weight='135'}//Student{id='003', height='171', weight='135'} }}class Student{ private String id; private String height; private String weight; public Student() { } public Student(String id, String height, String weight) { this.id = id; this.height = height; this.weight = weight; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getHeight() { return height; } public void setHeight(String height) { this.height = height; } public String getWeight() { return weight; } public void setWeight(String weight) { this.weight = weight; } @Override public String toString() { return "Student{" + "id='" + id + '\'' + ", height='" + height + '\'' + ", weight='" + weight + '\'' + '}'; }}
Arrays数组类
static String toString(T[] a); // 将a数组以字符串形式返回,用逗号隔开static T[] copyOf(T[] a,int length); // 拷贝一个数组static T[] copyOfRange(T[] a,int start,int end); // 拷贝一个数组,从start开始拷贝,到end-1,如果end带入a.length()默认值填充static void sort(T[] a); // 对数组进行排序static int binarySearch(T[] a,T e); // 进行二分查找数组,如果查找存在e元素,返回e的下标,没有这个元素就返回负值static void fill(T[] a,T e); // 将数组a的所有元素设置为estatic boolean equals(T[] a,T[] b); // 判断a数组和b数组是否相等,大小和下标相同对应元素是否相等
多维数组
在Java中本身没有多维数组,可以理解为一个一维数组中的元素也是一个数组,理解为数组的数组。
int[][] array = new int[][]{{1,2,3},{4,5,6}};// deepToString() 快速输出一个二维数组System.out.println(Arrays.deepToString(array)); // 输出 [[1, 2, 3], [4, 5, 6]]
注意:在初始化数组的时候,高维必须定义,而低维可以后面定义,这样就可以形成不规则数组。
int[][] array = new int[3][]; // 这个3这个高维必须定义,而后面的低维可以后面定义成其他数据for(int i=0;i<array.length();i++){ array[i] = new int[i+1]; // 定义低维}
2、面向对象程序设计
面向对象程序设计(
OOP
):程序由对象组成,每一个对象包含公开的特定功能和隐含的部分。
2.1、类和对象
类:是构造对象的模板。
对象:由类构造对象的过程称为创建类的实例,对象有三个特征,对象的行为、状态、标识。每一个对象都有自己的唯一标识。
类之间关系
- 依赖:一个类中使用了另一个类的方法和属性。
- 聚合:一个类中包含另一个类作为这个类的属性。
- 继承:一个类由另一个类继承,拥有另一个类的部分属性和部分方法。
LocalDate
类:表示日期的一个类,而Date是一个表示时间点的类。
static LocalDate now(); // 返回当前系统的日期 2022-4-5static LocalDate of(int year,int month,int day); // 创建一个有参数构建的日期,如果参数不合法会报错int getYear(); // 返回当前日期的年份int getMonthValue(); // 返回当前日期的月份int getDayOfMonth(); // 返回当前日期这个的月的天数DayOfWeek getDayOfWeek(); // 返回一个DayOfWeek对象,通过这个对象的getValue()方法返回一个1-7的数值,表示当天星期几LocalDate plusDays(int num); // 返回一个新的LocalDay对象,表示在当前日期后num天LocalDate minusDays(int num); // 返回一个新的LocalDay对象,表示在当前日期前num天
构造器:用于初始化类数字属性字段。
- 构造器与类同名。
- 每一个类都有一个或多个构造器。
- 构造器有0、1、多个参数。
- 构造器没有返回值,连void也没有。
- 构造器总是伴随new来调用的。
/** * @author tanKe * Date on 2022/4/5 21:09 */public class People { // 字段 private String name; // 无参数构造器 public People(){ System.out.println("你好"); } // 有一个参数构造器 public People(String name){ this.name = name; }}
null引用
一个对象包含一个对象的引用,也可以是一个特殊的值null,表示没有引用任何对象,如果一个为null的对象,然后调用它的方法会报NullPointerException
错误。所以在以后需要使用一个对象的方法时,需要明确这个对象是否为null。
类的访问权限:可以是使用public修饰类的数据,但是这种方式不安全,导致在任何地方都可以修改类中的数据。通常使用private修改属性,变成私有,但是在一个类,在方法中可以使用任何所属类的对象的私有数据。
class Student{ private String name; public void test(Student s){ // 虽然name是私有的,但是在类方法中依然可以使用,但必须是同一个类的所属对象 return this.name.equals(s.name); }}
final修饰符:可以修饰属性、类,修饰的属性,表示这个属性不能被更改一旦赋值后,修饰的类表示这个类的所有数据都不能修改,也可以表示这是一个最终类,不能被其他类继承。final修饰的属性是在构造方法后初始化的。
final修饰符,对于类型为基本数据类型和不可变类的字段有用,String就是一个不可变类,但是对于可变类会造成混乱。
// 不会指向另外的对象private final StringBuilder builder;// 错误builder = new StringBuilder();
2.2、静态修饰符 static
静态属性:使用static关键字修饰的属性成为静态属性,这个类的所有实例都共享这个属性,静态属性属于类,而不是某一个对象。即使这个对象为null,也可以使用这个类的静态属性。
public class Demo06 { public static String name = "张三"; // 所有这个类的对象实例相当于共享这个name,但是不属于某一个对象 public static void main(String[] args) { Demo06 d1 = null; System.out.println(d1.name); // 输出张三 Demo06 d2 = new Demo06(); d1.name = "李四"; // 修改name值 System.out.println(d2.name); // 输出李四 }}
静态方法:由static修饰的方法,静态方法不在对象上执行的方法,例如Math的pow()这个静态方法,直接通过
Math.pow()
使用,没有使用任何的Math对象。静态方法中不能使用非静态属性,但是可以使用静态属性。
- 方法不需要访问对象状态,参数都是通过显示参数传递时使用静态方法。
- 方法只需要访问类的静态属性。
静态工厂方法:静态方法还有一种用途就是使用静态工厂方法来创建一个对象。
为什么不使用构造器来实例对象呢?可能存在下面情况,需要静态工厂来实例对象。
- 构造器的名称必须和类名相同,但是我们有时无法命名构造器。
- 使用构造器时,无法改变构造对象的类型,而静态工厂方法可以起子类对象,来实现多态。
main方法:他是一个静态方法,所以不需要任何对象进行操作,启动程序时没有任何对象,通过静态main方法来实例对象。
2.3、方法参数
在Java中,总是采用按值传递。方法得到的所有参数值是一个副本。方法不能修改传递给它的任何参数变量的内容。一共有两种类型参数:基本数据类型、引用数据类型。
基本数据类型:
int a = 10;action(a); // 调用完后a的值没有发生变化System.out.println(a); // 输出10public void action(int a){ // a 相当于是一个副本,与数据没有关系 a = a + 10;}
引用数据类型:
Student y = new Student("101", "100", "100");action(y);System.out.println(y); // 输出id为100public static void action(Student x){ // y是x的引用副本,他们同时引用一个Student对象,这个方法结束后x引用就消失了,而y引用还是继续引用 x.setId("100"); // 当x修改了x引用的对象,y和x引用的同一个对象就发生改变}
在Java中,都是按值传递不是按引用调用,例如下面:
public static void swap(Student a,Student b){ Student temp = a; a = b; b = temp; // 方法结束后,a和b引用都会被丢弃}public static void main(Sting[] args){ Student s1 = new Student("1", "100", "100"); Student s2 = new Student("2", "100", "100"); swap(s1,s2); // s1和s2没有交换成功 System.out.println("s1=="+s1.toString()); // 输出1 System.out.println("s2=="+s2.toString()); // 输出2}
2.4、构造对象
重载:一个类中,方法名相同,参数不同,这就是方法的重载,在使用方法的时候,编译器会使用对应具体的方法。
// String类的构造器重载String();String(char[] a);String(int i);
无参构造器:没有参数的构造器,通过常在构造器中初始化属性值,如果没有写构造器,会自动提供一个无参构造器。
有参构造器:带有参数的构造器,也可以初始化属性值。
class Student{ private String name; // 无参构造器 public Student(){ name = "张三"; } // 有参构造器 public Student(String s){ name = s; }}
如果在实例化一个对象,在这个类中没有构造器,默认自动生成一个无参构造器,如果只存在有参构造器,会覆盖无参构造器,实例化时必须带参数,没有参数不能实例化。无参构造器和有参构造器可以同时存在。
调用另一个构造方法:在一个类中,有不同的构造方法,可以通过this(参数)来调用类中其它的构造方法。
class Student{ private String name; public Student(){ System.out.println("无参构造器"); } public Student(String name){ // 调用无参构造器,必须在构造器的第一行 this(); this.name = name; System.out.println("有参构造器"); }}
初始化代码块:只要构造对象时,就会执行代码块中的内容,但是在构造方法之前执行的。
class Student{ private String name; // 初始化代码块 { System.out.println("执行初始化代码块"); } public Student(String name){ this.name = name; }}
构造对象的执行过程:
- 如果构造其中第一行中调用了其他构造方法,则执行第二构造器。
- 没有调用其他构造器,初始化属性的默认值和初始化代码块。
- 执行构造器中的主体代码。
2.5、静态导入
有一种import
语句允许导入静态方法和静态属性,而不只是类。这样就可以省略调用静态方法、静态属性前面的类名。
import static java.lang.Math.*;// 这样就可以使用Math的静态方法和属性double sqrt = sqrt(100);System.out.println(PI);