类
类的源文件名必须与类同名。对于所有的类来说,类名的首字母应该大写。
如果没有显式为一个类指定构造方法,编译器会默认提供。
在创建一个类对象的时候,必须要有构造方法,类的构造方法名必须与类同名,且可以有多个。Java
中使用new
关键字创建对象。
一个Java
的源文件只能有一个Public
类,但可以有多个非Public
的类。源文件的名称需与Public
的类的类名保持一致。
Package
主要用来对Java
中众多的类、接口进行分类。
如果一个类定义在某个包中,那么 package
语句应该在源文件的首行。
包名通常使用小写的字母来命名避免与类、接口名字的冲突。
创建包:
///创建 ` learn.cls`的包
package learn.cls;
public class Person {
}
//在包中加入`Student`类,独立的源文件
package learn.cls;
public class Student extends Person {
}
包名必须要有与之对应的文件目录结构:
learn.cls
则必须要有learn/cls
这样的目录结构。
import
import
+ 路径 (包名,类名),用以定位类或源代码。
import learn.cls.*
编译器会从ClASSPATH/learn/cls
目录下查找对应的类。
如果源文件包含 import
语句,那么应该放在 package
语句和类定义之间。如果没有 package
语句,那么import
语句应该在源文件中最前面。
import
语句和 package
语句对源文件中定义的所有类都有效。在同一源文件中,不能给不同的类不同的包声明。
基本数据类型
Java
有两大数据类型
- 内置数据类型
- 引用数据类型
内置数据类型
有8种基本数据类型:
四种整型:
byte
: 8
位有符号的整数 范围 -128 ~ 127
short
: 16
位有符号的整数 范围 -2^15 ~ 2^15 - 1
int
: 32
位有符号的整数 范围 -2^31 ~ 2^31 - 1
long
: 64
位有符号的整数 范围 -2^63 ~ 2^63 - 1
两种浮点数:
float
: 单精度,32
位 小数
double
: 双精度,64
位 小数
其他:
boolean
: true
or false
char
: 单一的 16
位Unicode
字符
引用类型
对象、数组都是引用数据类型。
所有引用类型的默认值都是null
。
一个引用变量可以用来引用任何与之兼容的类型。
Java常量
在 Java
中使用final
关键字来修饰常量,声明方式和变量类似:
final double PI = 3.1415927;
byte、int、long、short
都可以用十进制、16
进制以及8
进制的方式来表示。
当使用字面量的时候,前缀 0
表示 8
进制,而前缀 0x
代表 16 进制, 例如:
int decimal = 100;
int octal = 0144;
int hexa = 0x64;
字符串常量和字符常量都可以包含任何Unicode
字符。例如:
char a = '\u0001';
String a = "\u0001";
自动类型转换
整型、实型(常量)、字符型数据可以混合运算。运算中,不同类型的数据先转化为同一类型,然后进行运算。
转换顺序由低到高依次为:
byte, short, char -> int->long->float->double
数据类型转换必须满足如下规则:
- 不能对boolean类型进行类型转换。
- 不能把对象类型转换成不相关类的对象。
- 在把容量大的类型转换为容量小的类型时必须使用强制类型转换。
- 浮点数到整数的转换是通过舍弃小数得到,而不是四舍五入.
强制类型转换
int x = 128;
byte y = (byte) x; // y = -128 位数不足,溢出
double d = 7.9D;
int e = (int)d; //7
整数的默认类型为int
;
小数默认是 double
类型,在定义 float
类型时必须在数字后面跟上 F
或者 f
。
Java的变量
Java
中变量的声明与其他语言大同小异,基本都是 Type Identifier = value
。
局部变量
局部变量不会有默认值,访问修饰符也不能作用于局部变量,必须初始化才能使用
public void pupAge(){
///错误
int age;
///正确
int age = 0;
age = age + 7;
System.out.println("小狗的年龄是 : " + age);
}
实例变量
当一个对象被实例化之后,每个实例变量的值就跟着确定;实例变量在对象创建的时候创建,在对象被销毁的时候销毁;实例变量可以声明在使用前或者使用后;访问修饰符可以修饰实例变量;实例变量具有默认值。数值型变量的默认值是0
,布尔型变量的默认值是false
,引用类型变量的默认值是null
。变量的值可以在声明时指定,也可以在构造方法中指定;
Java的修饰符
Java
中的修饰符有两种:
访问修饰符
用来对类、方法、构造方法、变量的访问设置不同的权限。
- public : 范围:所有类可见;修饰对象:类、方法、接口、变量
- protected : 范围:同一包中的类和所有子类可见,修饰对象:方法、变量;不能修饰类
- private:范围:同一类内可见,修饰对象:方法、变量;不能修饰类
- default:默认访问修饰符,不使用任何关键字;同一包中可见;修饰对象:类、接口、方法、变量。
接口里的变量都隐式声明为 public static final
,而接口里的方法默认情况下访问权限为 public
。
protected
修饰符有个两个点需要分析说明:
- 子类与基类在同一个包内,被声明为
protected
的变量、方法和构造器能被同一个包中的任何其他类访问。 - 子类与子类不在同一个包内,那么在子类中,子类实例可以访问其从基类继承而来的
protected
方法,而不能访问基类实例的protected
方法。示例:
///基类在包`Animals `中
package Animals;
public class Animals {
protected void run() {
System.out.print("跑");
}
protected void eat() {
System.out.print("吃饭");
}
}
///子类在包 learn.cls中
package learn.cls;
import Animals.Animals;
public class Dog extends Animals {
@Override
protected void eat() {
this.run();
System.out.print("吃屎");
}
}
// 在包learn.cls的其他类中创建Dog实例并访问基类被protected修饰的方法
Dog dog = new Dog();
dog.eat(); ///✅
dog.run();//无法访问,编译器会报错 ❌
///但是基类的run方法在子类是可在直接访问的,
子类能访问 protected
修饰符声明的方法和变量,这样就能保护不相关的类使用这些方法和变量。如果我们只想让该方法对其所在类的子类可见,则将该方法声明为 protected
。
访问控制和方法继承规则
- 父类声明的
public
方法,在子类中也必须为public
; - 父类声明的
protected
方法,在子类中要么为Public
,要么protected
但不能为private
- 父类声明的
private
方法,不能被子类继承;
非访问修饰符
-
static
修饰符,用来修饰类方法和类变量。 -
final
修饰符,用来修饰类、方法和变量,final 修饰的类不能够被继承,修饰的方法不能被继承类重新定义,修饰的变量为常量,是不可修改的。 -
abstract
修饰符,用来创建抽象类和抽象方法。如果一个类包含抽象方法,那么该类一定要声明为抽象类。 -
synchronized
和volatile
修饰符,主要用于线程的编程。
synchronized
关键字声明的方法同一时间只能被一个线程访问。synchronized
修饰符可以应用于四个访问修饰符。
volatile
修饰的成员变量在每次被线程访问时,都强制从共享内存中重新读取该成员变量的值。而且,当成员变量发生变化时,会强制线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。
一个 volatile
对象引用可能是null
。
///`synchronized `
public synchronized void showDetails(){
.......
}
///`volatile `
private volatile boolean active;
transient
修饰符:序列化的对象包含被 transient
修饰的实例变量时,java
虚拟机(JVM)跳过该特定的变量。即:用它修饰的变量被序列化不会被持久化。
抽象方法是一种没有任何实现的方法,该方法的具体实现由子类提供。抽象方法不能被声明成 final
和 static
。任何继承抽象类的子类必须实现父类的所有抽象方法,除非该子类也是抽象类。
如果一个类包含若干个抽象方法,那么该类必须声明为抽象类。抽象类可以不包含抽象方法。
抽象方法的声明以分号结尾,例如:public abstract sample();
。
Java 运算符
Java
中的运算符与其他语言运算符基本一致,分为:
- 算数运算符
- 自增/减运算符:a++、a--、++a、--a
- 关系运算符
- 位运算符: 与运算
同1 ? 1 : 0
或运算:同0 ? 0 : 1
异或位值相同 ? 0 : 1
- 逻辑运算符
- 赋值运算符
- 条件运算符、三元运算符
特有的运算符:
instanceof
运算符: 该运算符用于操作对象实例,检查该对象是否是一个特定类型(类类型或接口类型)。 运算符使用的格式: ( Object reference variable ) instanceof (class/interface type)
boolean istrue = "abcd" instanceof String; // result: true
Java的循环结构
Java
中有三种主要的循环结构,和其他语言一致,分为:
-
while
循环 -
do…while
循环 -
for
循环
其中for
循环在Java
中另一种使用方式:增强for
循环:
int[] numbers = {1,2,3,4,5};
for(int item : numbers) {
System.out.println(item);
}
Java的Number与Math
Numer
:将基本数据类型的包装成类,java
中的基本数据类型都有自己的包装类。
///左侧:基本数据类型
///右侧:包装后的类
byte -> Byte //extends Number extends Object
short -> Short //extends Number extends Object
int -> Integer //extends Number extends Object
long -> Long //extends Number extends Object
float -> Float //extends Number extends Object
double -> Double //extends Number extends Object
boolean -> Boolean //extends Object
char -> Character //extends Object
Boolean boolobj = Boolean.TRUE;
Character charobj1 = 'e'; //e
Character charobj2 = '\u0065'; //e
System.out.println(charobj);
关于运算
Integer x = 5;
int y = 10
x = x + y;
System.out.println(x); //15
关于Math
int a = -10;
Integer b = -40;
a = Math.abs(a);
b = Math.abs(b);
a = a + b;
System.out.println(a); // 50
Java中的Character
Character
类用于对单个字符进行操作。
将一个char
类型的参数传递给需要一个Character
类型参数的方法时,那么编译器会自动地将char
类型参数转换为Character
对象
Character charobj1 = 'e'; //e
Character charobj2 = '\u0065'; //e
if (Character.isDigit(charobj1)) {
System.out.println("是数字");
}
String str = Character.toString(charobj2);
System.out.println(str);//e
Java中的字符串
- 创建字符串
String string = "String"; /// 存储在公共池
String heapStr = new String("String"); /// 存储在堆中
char[] chars = {'h','e','l','l','o'};
String heapStrByChar = new String(chars);
System.out.printf("公共池:%s,堆字符串:%s\n",string,heapStrByChar);//公共池:String,堆字符串:hello
- 字符串操作
///获取字符串长度
int length = heapStrByChar.length();/// 5
///拼接两个字符串
string = heapStrByChar.concat(string); //Stringhello
///字符串格式化
String format = String.format("公共池:%s,堆字符串:%s\n",string,heapStrByChar);
System.out.println(format);
注意:String
类是不可改变的,所以一旦创建了 String
对象,那它的值就无法改变了
如果需要改变需要用到StringBuffer
和StringBuilder
///StringBuilder
///参数提示 cmd + p
StringBuilder sb = new StringBuilder(10);
sb.append("hello");
sb.insert(5,'!');
sb.append(new char[]{'B','o','b'});
System.out.println(sb);//hello!Bob
sb.delete(3,6);
String finalStr = sb.toString();
System.out.println(finalStr);///helBob
///StringBuffer
StringBuffer sbf = new StringBuffer("hello");
sbf.append('!');
sbf.append(new char[]{'j','a','v','a'});
sbf.insert(0,"smile");
sbf.insert(5,' ');
System.out.println(sbf);//smile hello!java
sbf.deleteCharAt(11);
String res = sbf.toString();
System.out.println(res);//smile hellojava
StringBuilder
类在 Java 5
中被提出,它和 StringBuffe
r 之间的最大不同在于 StringBuilder
的方法不是线程安全的(不能同步访问)。
由于 StringBuilder
相较于 StringBuffer
有速度优势,所以多数情况下建议使用 StringBuilder
类。
Java数组
- 数组声明
///首选
dataType [] arrayVar;
///效果一致,非首选
dataType arrayVar[];
- 数组的创建
int[] array = new int[10];
array[0] = 1;
array[1] = 2;
int[] array = {1,2,3,4,4,5};
for (int x : array) {
System.out.println(x);
}
- 多维数组声明
///格式
type[][] typeName = new type[typeLength1][typeLength2];
///示例 2行3列的二维整型数组
int[][] intArray = new int[2][3];
- 多维数组的创建
int[] arr = {1,2,3};
int[][] array1 = new int[2][3];
array1[1][0] = 2;
array1[1][1] = 3;
array1[1][2] = 4;
array1[0] = arr;
for (int[] item : array1) {
for (int x : item) {
System.out.println(x);
}
}
- 通过
Arrays
类的类方法,执行对数组的操作
int[] array = {1,2,3,4,4,5};
int[] arr = {1,2,3};
Arrays.sort(array);
Arrays.equals(array,arr);
///...
Java日期
-
Date
日期创建
Date date = new Date();
///y:1900 + 119 = 2019 ; m: 0~11
Date date1 = new Date(119,10,20);
// 1$ 表示第一个参数 %t 表示打印时间 %n 换行 %tF 表示以格式`%tY-%tm-%td`输出日 %tb/%tB 月份的全称与简称
System.out.printf("%1$tY:%1$tm:%1$td %1$tB%n %2$tF%n",date,date1);
///2022:02:17 二月
///2019-11-20
-
Date
日期格式化
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String tfs = df.format(date);
System.out.println(tfs);
- 字符串转日期
Date
SimpleDateFormat df1 = new SimpleDateFormat("yyyy-MM-dd");
String dateStr = "2022-10-01";
try {
Date date2 = df1.parse(dateStr);
System.out.printf("%tD%n",date2);//10/01/22
} catch (ParseException e) {
System.out.println("解析出错");
}
日期格式化输出
详见:https://www.runoob.com/w3cnote/java-printf-formate-demo.htmlCalendar
的创建与使用
Calendar
类是一个抽象类,在实际使用时使用其特定的子类的对象,创建对象的过程对程序员来说是透明的,只需要使用getInstance
方法创建即可。
///默认当前日期
Calendar cld = Calendar.getInstance();
///可修改为表示2018年12月15日的日历对象
/// month : 0 ~ 11
cld.set(2018,11,15);
///将天数修改为10
cld.set(Calendar.DATE,10);
System.out.printf("%tF%n",cld.getTime());//2018-12-10
// 获得年份
int year = cld.get(Calendar.YEAR);
// 获得月份
int month = cld.get(Calendar.MONTH) + 1;
// 获得日期
int day = cld.get(Calendar.DATE);
System.out.printf("%s:%s:%s%n",year,month,day);
-
GregorianCalendar
GregorianCalendar
是Calendar
类的一个具体实现。Calendar
的getInstance()
方法返回一个默认用当前的语言环境和时区初始化的GregorianCalendar
对象。GregorianCalendar
定义了两个字段:AD
和BC
。这是代表公历定义的两个时代。
GregorianCalendar gcld = new GregorianCalendar();
GregorianCalendar gcld1 = new GregorianCalendar(2019,11,30,23,59,59);
System.out.printf("%tc%n",gcld1.getTime());//周一 12月 30 23:59:59 CST 2019
///其他与`Canlendar`一致
Java正则表达式
Java
中的正则使用主要包括三个类:Pattern
、Matcher
、PatternSyntaxException
- Java正则表达式的语法
详见:https://www.runoob.com/java/java-regular-expressions.html - Java正则表达式的使用
///检索字符串是否包含与正则匹配的子字符串
String content = "wowaattterww";
///.* regex .* 用来查找字符串中是否包含被`regex`匹配到的字串
String regex = ".*wa{1,2}t{1,3}er.*"; //它匹配 waater/waatter/waattter等
boolean match = Pattern.matches(regex,content);
System.out.println(match);///true
- 捕获组
捕获组是把多个字符当一个单独单元进行处理的方法,它通过对括号内的字符分组来创建。例如,正则表达式 (dog) 创建了单一分组,组里包含"d","o",和"g"。
捕获组是通过从左至右计算其开括号来编号。例如,在表达式:"((A)(B(C)))"
,有四个这样的组:((A)(B(C))))
、(A)
、(B(C))
、(C)
String content = "wowaattterww888.com";
///分4组,以左括号划界
/// 0 组 以`reg_p`为整体
// 1 组 只匹配`(\D*)`: 匹配非数字字符 0个或多个 与反向范围字符[^0-9]等效
// 2 组 只匹配 `(\d+)`: 匹配数字字符 1个或多个 与范围字符[0-9]等效
// 3 组 只匹配 `(.*)` : 匹配单个字符 0个或多个
String reg_p = "(\\D*)(\\d+)(.*)";
Pattern pattern = Pattern.compile(reg_p);
///只能通此来创建`Matcher`类
Matcher matcher = pattern.matcher(content);
//按照惯例,第0组表示整个模式。它不包括在此计数中。
System.out.println("该正则有"+ (matcher.groupCount() + 1) + "个分组\n");//该正则有4个分组
//Attempts to find the next subsequence of the input sequence that matches the pattern.
boolean found = matcher.find();///匹配到了
if (found) {
System.out.printf("第一个捕获组的匹配到的值:%s 开始索引:%d,结束索引:%d%n",matcher.group(0),matcher.start(0),matcher.end(0));//wowaattterww888.com
System.out.printf("第二个捕获组的匹配到的值:%s 开始索引:%d,结束索引:%d%n",matcher.group(1),matcher.start(1),matcher.end(1));//wowaattterww888.com
System.out.printf("第三个捕获组的匹配到的值:%s 开始索引:%d,结束索引:%d%n",matcher.group(2),matcher.start(2),matcher.end(2));//wowaattterww888.com
System.out.printf("第四个捕获组的匹配到的值:%s 开始索引:%d,结束索引:%d%n",matcher.group(3),matcher.start(3),matcher.end(3));//wowaattterww888.com
}
/*
第一个捕获组的匹配到的值:wowaattterww888.com 开始索引:0,结束索引:19
第二个捕获组的匹配到的值:wowaattterww 开始索引:0,结束索引:12
第三个捕获组的匹配到的值:888 开始索引:12,结束索引:15
第四个捕获组的匹配到的值:.com 开始索引:15,结束索引:19
*/
-
Matcher
关键方法:lookingAt
与matches
///lookingAt与matches
String content = "waattterwo"; ///
String regex = "wa{1,2}t{1,3}er"; //它匹配 waater/waatter/waattter等
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(content);
///输出:lookingAt匹配?true matches匹配?false
System.out.println("lookingAt匹配?"+ m.lookingAt() + " matches匹配?"+ m.matches() + "\n" );
lookingAt
与matches
方法的区别,matches
要求整个字符串都要与正则匹配,而lookingAt
不要求,但是它要求从字符串的开始位置就要与正则匹配,否则就是false
。如果将上述代码:String content = "1waattterwo";
,则lookingAt
会返回false
。
-
Matcher
关键方法:replaceFirst
与replaceAll
replaceFirst
和replaceAll
方法用来替换正则表达式匹配到的文本。不同的是,replaceFirst
替换首次匹配,replaceAll
替换所有匹配。
String content = "waa3t2t2t5erwo";
String regex = "\\d+"; //匹配数字字符 1个或多个
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(content);
String firstReplace = m.replaceFirst("数字");
String allReplace = m.replaceAll("数字");
///输出:replaceFirst:waa数字t2t2t5erwo replaceAll:waa数字t数字t数字t数字erwo
System.out.printf("replaceFirst:%s replaceAll:%s%n",firstReplace,allReplace);
-
Matcher
关键方法:appendReplacement
与appendTail
Matcher
类也提供了appendReplacement
和appendTail
方法用于文本替换:
String content = "waa3t2t2t5erwo";
String regex = "\\d+"; //匹配数字字符 1个或多个
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(content);
StringBuilder sb = new StringBuilder();
while (m.find()) {
m.appendReplacement(sb,"数字");
}
System.out.println(sb.toString());//waa数字t数字t数字t数字
m.appendTail(sb);
System.out.println(sb.toString());//waa数字t数字t数字t数字erwo
Java中的方法
大多与其他语言一致,区别如下:
- 构造方法
构造方法和它所在类的名字相同,但构造方法没有返回值。
Java
自动为所有的类提供了一个默认构造方法,默认构造方法的访问修饰符和类的访问修饰符相同(类为 public
,构造函数也为 public
;类改为 protected
,构造函数也改为 protected
)。一旦定义自己的构造方法,默认构造方法就会失效。
// 一个简单的构造函数
class MyClass {
int x;
// 以下是构造函数
MyClass(int i ) {
x = i;
}
MyClass() {
x = 10;
}
}
- 可变参数
格式:typeName... parameterName
可当做数组用
finalize() 方法
Java 9
该方法被弃用了。
Java
允许定义这样的方法,它在对象被垃圾收集器析构(回收)之前调用,这个方法叫做finalize( )
,它用来清除回收对象。
///` finalize( )`方法的一般格式
protected void finalize()
{
// 在这里终结代码
}
关键字 protected
是一个限定符,它确保finalize()
方法不会被该类以外的代码调用。
Java中的Stream、File、IO
Stream
可以理解为一个数据的序列。输入流表示从一个源读取数据,输出流表示向一个目标写数据。
- 控制台输入与输出
///接收控制台输入的输入流
InputStreamReader reader = new InputStreamReader(System.in);
char c;
do {
c = (char) reader.read(); ///控制台输入完成后,读取控制台输入的字符,one by one
///输入 wwwwwwqq 输出 wwwwwwq
System.out.println(c);
} while ( c != 'q');
///转一下,获取字符流,读取整行
BufferedReader bufferedReader = new BufferedReader(reader);
String readStr = bufferedReader.readLine();
System.out.println(readStr);
- 文件读写
创建一个文件写入数据,进行读取输出到控制台,然后删除该文件。
///创建输出流,写入项目根目录
File dir = new File("./test/tmp/");
dir.mkdirs();//会创建中间目录
///文件不存在,写入过程中会自动创建
File file = new File("./test/tmp/test.text");
try {
///此类写入的是字节流
FileOutputStream fileOutputStream = new FileOutputStream(file);
///包装一层写入 编码后的字符串
OutputStreamWriter writer = new OutputStreamWriter(fileOutputStream,"utf-8");
writer.write("你好,");
writer.append("我是鲍勃\n");
///关闭写入流
writer.close();
///关闭输出流
fileOutputStream.close();
///读取文件的内容
FileInputStream fileInputStream = new FileInputStream("./test/tmp/test.text");
///读取解码后的解码字符串
InputStreamReader reader = new InputStreamReader(fileInputStream,"utf-8");
///读取方式1;
///ready 文件不空 返回
StringBuilder sb = new StringBuilder();
while (reader.ready()){
sb.append((char) reader.read());
};
System.out.println(sb.toString());
///读取方式2:
///返回文件预估长度,字节数
///int charLength = fileInputStream.available() / 2;
///char[] chars = new char[charLength];
///charLength = reader.read(chars); ///返回读到的字符数
///String res = new String(chars,0,charLength);
///System.out.println(res);
///关闭读取流
reader.close();;
///关闭输入流
fileInputStream.close();
///删除文件
System.out.println(file.delete());
///删除目录 tmp
dir.delete();
///删除父级目录 test
dir.getParentFile().delete();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
Java中的Scanner类
Java 5
新特性, 它也可以获取输入数据。
///文件输出
File file = new File("./test/tmp/test.text");
Scanner scanner = new Scanner(file);
///皆可
while (scanner.hasNext()) {
System.out.println(scanner.next());///无法获取空格
}
while (scanner.hasNextLine()) {
System.out.println(scanner.nextLine());///可以获取空格
}
scanner.close();
///获取用户键盘输入,进行控制台输出
Scanner scanner1 = new Scanner(System.in);
while (scanner1.hasNext()) {
System.out.println(scanner1.next());
}
scanner1.close();
Java中异常
与其他语言,差别不大,具体细节:https://www.runoob.com/java/java-exceptions.html
Java中的继承
与其他语言差别不大,关键字extends
、implements
、final
- 构造方法
子类是不继承父类的构造函数的。
如果父类的构造器带有参数,则必须在子类的构造器中显式地通过super
关键字调用父类的构造器并配以适当的参数列表。
如果父类构造器没有参数,则在子类的构造器中不需要使用super
关键字调用父类构造器,系统会自动调用父类的无参构造器。
class SuperClass {
private int n;
SuperClass(){
System.out.println("SuperClass()");
}
SuperClass(int n) {
System.out.println("SuperClass(int n)");
this.n = n;
}
}
// SubClass 类继承
class SubClass extends SuperClass{
private int n;
SubClass(){ // 自动调用父类的无参数构造器
System.out.println("SubClass");
}
public SubClass(int n){
super(300); // 调用父类中带有参数的构造器
System.out.println("SubClass(int n):"+n);
this.n = n;
}
}
// SubClass2 类继承
class SubClass2 extends SuperClass{
private int n;
SubClass2(){
super(300); // 调用父类中带有参数的构造器
System.out.println("SubClass2");
}
public SubClass2(int n){ // 自动调用父类的无参数构造器
System.out.println("SubClass2(int n):"+n);
this.n = n;
}
}
- implements
使用implements
关键字可以变相的使java
具有多继承的特性,使用范围为类继承接口的情况,可以同时继承多个接口(接口跟接口之间采用逗号分隔)。
public interface A {
public void eat();
public void sleep();
}
public interface B {
public void show();
}
public class C implements A,B {
}
- final
final 关键字声明类可以把类定义为不能继承的,即最终类;或者用于修饰方法,该方法不能被子类重写。
Java的重写(Override)与重载(Overload)
- 重写(Override)
重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!
重写的规则:
1.返回类型为被重写方法的返回类型及其子类。
2.访问权限不能比父类中被重写的方法的访问权限更低。
3.声明为 static 的方法不能被重写,但是能够被再次声明。
4.构造方法不能被重写。
5.重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。 - 重载(Overload)
在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。
每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。
最常用的地方就是构造器的重载。
Java多态
三要素继承、重写、父类指针指向子类。与其他语言一样。
Java抽象类&抽象方法
关键字abstract
。
规则:
1.抽象类,不能被实例化;
2.抽象类可以没有抽象方法,但有抽象方法的类必定要是抽象类
3.抽象类中定义的抽象方法,必须要被子类 重写,除非子类也是抽象类
4.构造函数、Static
修饰的函数,不能为抽象方法。
5.抽象方法只包含一个方法名,而没有方法体
Java 接口
Java
接口是抽象方法的集合,关键字interface
;一个类通过继承接口的方式,来继承接口中的抽象方法。接口和类是不同的概念。类实现接口,而非继承接口。
Java
中接口可以用来声明变量,可指向空指针,也可以指向实现次接口的实例对象。
实现接口的类必须实现接口的所有方法,除非该类为抽象类。
接口特性:
1.接口中的每个方法都是隐式抽象;接口在声明的时候也是隐式抽象的; 隐式指定为public abstract
。
2.接口中可以有变量,但只能是public static final
修饰的变量。
-
Java8
中可以定义接口的默认方法实现(有方法体),关键字default
。 -
Java8
中可以定义static
方法实现。
实现接口的关键字: implements
,接口实现的规则:
1.类在实现接口方法时,无法强制抛出异常;可在接口中或实现接口的抽象类中抛出该强制异常。
2.一个类可以实现多个接口。
3.一个接口可以继承另一个接口。
示例:
public interface Animals {
void behavior();
void eat();
void die();
}
public interface Beasts extends Animals{
///定义接口方法
void run();
///定义默认方法实现
default void behavior() {
System.out.printf("%s行为:地上跑%n", Beasts.BEASTS);
}
///定义变量
static final String BEASTS = "走兽";
///定义静态方法
static String category() {
return BEASTS + "类";
}
}
//可多继承,比如 `public interface Birds extends Animals,Beasts`
public interface Birds extends Animals {
///定义接口方法
void fly();
///默认实现`Animals`的方法
default void behavior() {
System.out.printf("%s行为:天上飞%n", BIRDS);
}
///定义变量
static final String BIRDS = "飞禽";
///定义静态方法
static String category() {
return BIRDS + "类";
}
}
///飞虎!哇
public class FlyTiger implements Beasts, Birds {
@Override
public void behavior() {
Birds.super.behavior();
Beasts.super.behavior();
System.out.println("飞虎行为怪异\n");
}
@Override
public void eat() {
System.out.println(Beasts.category()+":飞虎吃饭");
System.out.println(Birds.category()+":飞虎吃饭");
}
@Override
public void run() {
System.out.println("飞虎在地");
}
@Override
public void die() {
System.out.println("飞虎在地下");
}
@Override
public void fly() {
System.out.println("飞虎在天");
}
}
///使用
public static void main(String[] args) {
Birds birds = new FlyTiger();
birds.fly();
Beasts beasts = new FlyTiger();
beasts.run();
FlyTiger flyTiger = new FlyTiger();
flyTiger.eat();
flyTiger.behavior();
flyTiger.die();
}
Java 枚举
- 枚举声明
enum Color {
BLUE,
RED,
GREEN
}
- 枚举使用
enum Color {
BLUE,
RED,
GREEN
}
public class EnumTest {
enum Version {
DEV,
RELEASE,
TRAIL
}
public static void main(String[] args) {
Color color = Color.RED;
Version version = Version.DEV;
System.out.println(version);///DEV
System.out.println(color);///RED
}
}
- 枚举的内部实现
enum Color {
BLUE,
RED,
GREEN
}
///内部通过`class`实现
class Color {
public static final Color RED = new Color();
public static final Color BLUE = new Color();
public static final Color GREEN = new Color();
}
- 关键方法
for (Version v : Version.values()) {///Version.values() 枚举的所有值
System.out.println(v);
System.out.println(v.ordinal());//索引
System.out.println(Version.valueOf("DEV"));//取值
}
枚举跟普通类一样可以用自己的变量、方法和构造函数,构造函数只能使用 private 访问修饰符,所以外部无法调用。
public class EnumTest {
enum Version {
DEV,
RELEASE,
TRAIL;
private Version(){
System.out.println("Version枚举构造函数被"+this.name() + "调用\n");
}
public String getInfo() {
return "版本信息" + this.name();
}
}
public static void main(String[] args) {
Version version = Version.RELEASE;///构造函数被调用
String info = version.getInfo();///信息打印
System.out.println(info);
}
}
- 枚举可实现抽象方法
enum Color {
BLUE{
public String getInfo() {
return "蓝色";
}
},
RED{
public String getInfo() {
return "红色";
}
},
GREEN{
public String getInfo() {
return "绿色";
}
};
public abstract String getInfo();
}
public class EnumTest {
public static void main(String[] args) {
for(Color color : Color.values()) {
System.out.println(color.getInfo());
}
}
}
Java集合框架
Java
集合框架主要包括两种类型的容器:集合(Collection
)、键值对(Map
)。
Collection
接口有3
种子类型List
、Set
和Queue
也是接口。具体可实例化的集合类继承了实现上述接口的抽象类。
集合中存储Java
对象。常用的可实例化的集合类:ArrayList
、LinkedList
、HashSet
。常用的Map
可实例化的类:HashMap
。
ArrayList
ArrayList
类是一个可以动态修改的数组,与普通数组的区别就是它是没有固定大小的限制,我们可以添加或删除元素。
- ArrayList的继承与接口实现关系
///ArrayList的继承与接口实现关系
public interface Collection extends Iterable
public interface List extends Collection
public abstract class AbstractCollection implements Collection
public abstract class AbstractList extends AbstractCollection implements List
public class ArrayList extends AbstractList implements List, RandomAccess, Cloneable, java.io.Serializable
- ArrayList的关键操作
ArrayList arrayList = new ArrayList<>();///创建
arrayList.add(100);///添加对象
arrayList.add(300);
arrayList.add(200);
///小->大排序
arrayList.sort((o1, o2) -> {
return o1.compareTo(o2);
});
Collections.sort(arrayList);///小->大
///修改
arrayList.set(1, 250);
///查询
arrayList.get(0);
///删除
arrayList.remove(1);
///删除所有
arrayList.clear();
///遍历一
for (int i = 0; i < arrayList.size(); i++) {
System.out.println(arrayList.get(i));
}
///遍历二
for (Integer i : arrayList) {
System.out.println(i);
}
///遍历三:可以利用iterator.remove()方法删除元素
Iterator iterator = arrayList.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
LinkedList
LinkedList
:链表,是一种常见的数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的地址。链表可分为单向链表和双向链表。与ArrayList
类似,但LinkedList
的增加和删除的操作效率更高,而查找和修改的操作效率较低。
以下情况使用 ArrayList
:
1.频繁访问列表中的某一个元素。
2.只需要在列表末尾进行添加和删除元素操作。
以下情况使用 LinkedList
:
1.需要通过循环迭代来访问列表中的某些元素。
2.需要频繁的在列表开头、中间、末尾等位置进行添加和删除元素操作。
-
LinkedList
的继承与接口实现关系
public interface Collection extends Iterable
public interface List extends Collection
public abstract class AbstractCollection implements Collection
public abstract class AbstractList extends AbstractCollection implements List
///多了一层
public abstract class AbstractSequentialList extends AbstractList
public class LinkedList extends AbstractSequentialList implements List, Deque, Cloneable, java.io.Serializable
-
LinkedList
的关键方法
///创建
LinkedList linkedList = new LinkedList<>();
///添加
linkedList.add("java");
linkedList.add("flutter");
linkedList.add("objc");
linkedList.add("swift");
linkedList.add("shell");
///大-> 小 排序
linkedList.sort((o1, o2) -> {
return o2.compareTo(o1);
});
Collections.sort(linkedList);///小->大
///修改
linkedList.set(4,"php");
///删除
linkedList.remove("php");
//linkedList.remove(3);
///查询
linkedList.get(0);
///其他
linkedList.addFirst("vue");
linkedList.addLast("js");
linkedList.removeFirst();
linkedList.removeLast();
///遍历一
for (int i = 0; i < linkedList.size(); i++) {
System.out.println(linkedList.get(i));
}
///遍历二
for (String str :
linkedList) {
System.out.println(str);
}
///遍历三: 可以利用iterator.remove()方法删除元素
Iterator iterator = linkedList.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
HashSet
HashSet
无序的,不允许有重复元素的集合,它不是线程安全的。如果多个线程尝试同时修改HashSet
,则最终结果是不确定的。 必须在多线程访问时显式同步对 HashSet
的并发访问。
-
HashSet
的继承与接口实现关系
public interface Collection extends Iterable
public interface Set extends Collection
public abstract class AbstractCollection implements Collection
public abstract class AbstractSet extends AbstractCollection implements Set
public class HashSet extends AbstractSet implements Set, Cloneable, java.io.Serializable
-
HashSet
关键方法
///创建
HashSet hashSet = new HashSet<>();
///添加
hashSet.add(null);//可添加null
hashSet.add("张飞");
hashSet.add("关羽");
hashSet.add("刘备");
hashSet.add("赵云");
hashSet.add("赵云");//无法添加重复元素
///删除
hashSet.remove("赵云");
///删除所有
hashSet.clear();
///包含
if (hashSet.contains("刘备")) {
System.out.println("包含");
}
///遍历
for (Object obj: hashSet) {
System.out.println(obj);
}
HashMap
HashMap
是一个散列表,它存储的内容是键值对(key-value
)映射。
HashMap
实现了 Map
接口,根据键的 HashCode
值存储数据,具有很快的访问速度,最多允许一条记录的键为null
,不支持线程同步。
HashMap
是无序的,即不会记录插入的顺序。
HashMap
的 key
与 value
类型可以相同也可以不同,可以是字符串(String
)类型的 key
和 value
,也可以是整型(Integer
)的 key
和字符串(String
)类型的 value
。
-
HashMap
的继承与接口实现关系
public interface Map
public abstract class AbstractMap implements Map
public class HashMap extends AbstractMap implements Map, Cloneable, Serializable
-
HashMap
的关键方法
///创建
HashMap map = new HashMap();
///添加
map.put(1,"1");
map.put(2,"2");
map.put(3,"3");
///如果没有则添加
map.putIfAbsent(2,"4");
///修改
map.replace(2,"20");
map.put(2,"22");
///删除
map.remove(3);
//获取
map.get(2);///22
System.out.println(map);
///遍历一
for (Integer key : map.keySet()) {
System.out.println(map.get(key));
}
///遍历二
for (String value:map.values()) {
System.out.println(value);
}
///遍历三
Iterator> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next().getValue());
}
///遍历四
ArrayList arrayList = new ArrayList<>(map.values());
for (int i = 0; i < arrayList.size(); i++) {
System.out.println(arrayList.get(i));
}
Java中的Object
Java
中Object
类是所有类的父类,也就是说 Java
的所有类都继承了 Object
,子类可以使用 Object
的所有方法。
Object
类可以显示继承,也可以隐式继承,以下两种方式时一样的:
///显式继承
pulic class Test extends Object {
}
///隐式继承
public class test {
}
Java中的泛型
泛型方法
定义泛型方法的规则:
1.泛型方法需要声明类型参数,类型参数声明在函数返回值类型的前面,采用尖括号< >
包裹。形如:
2.类型参数声明可有多个,尖括号内,彼此用逗号,
隔开
3.类型参数能被用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符。
4.泛型方法体的声明和其他方法一样。
5.注意类型参数只能代表引用型类型,不能是原始类型(像 int、double、char 等)。
///示例
public static T genericsFunc(T param1, S param2) {
System.out.println(param2);
return param1;
}
泛型类
类名后面添加了类型参数声明部分,类型参数声明部分也包含一个或多个类型参数,参数间用逗号隔开。
public class GenericTest {
T type;
U user;
public GenericTest(T type, U user){
this.type = type;
this.user = user;
}
public T getType(){
return this.type;
}
public U getUser(){
return this.user;
}
public void setType(T type) {
this.type = type;
}
public void setUser(U user) {
this.user = user;
}
public static void main(String[] args) {
GenericTest genericTest = new GenericTest<>("工人","张三");
genericTest.setType("农民");
System.out.println(genericTest.type);
System.out.println(genericTest.user);
}
}
类型通配符与条件限定
类型通配符一般是使用 ?
代替具体的类型参数。例如 List>
在逻辑上是 List
等所有 List<具体类型实参>
的父类。不需要额外声明的类型参数。
public static void printArray(List> arr) {
System.out.println(arr.get(0));
}
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add("hello");
printArray(list);
}
- 限定通配符的类型为特定类型的子类:
extends T>
public static void printArray(List extends Number> arr) {
System.out.println(arr.get(0));
}
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add("hello");
//printArray(list);//报错
ArrayList numlist = new ArrayList();
numlist.add(100);
printArray(numlist);
}
- 限定通配符的类型为特定类型的父类:
super T>
public static void printArray(List super Integer> arr) {
System.out.println(arr.get(0));
}
Java序列化
Java
序列化,是将一个Java
类对象序列化为字节序列的过程,该字节序列会保存对象的数据,也会保存对象类型关联的所有类型的信息;因此该字节序列也可以通过反序列化转化为对象。整个过程基于JVM
,因此该字节序列支持平台迁移。
Java
的对象支持序列化的条件,该对象所属类型必须实现接口java.io.Serializable
Java
序列化机制的主要类ObjectOutputStream
与ObjectInputStream
。
- 序列化
当序列化一个对象到文件时, 按照Java
的标准约定是给文件一个.ser
扩展名。
public class SerializableTest implements java.io.Serializable {
public String name;
public String job;
public int age;
public boolean hasJob;
public transient String intro; ///无法被序列化
}
public static void serialization() {
///创建对象
SerializableTest testObjc = new SerializableTest();
testObjc.name = "波波";
testObjc.job = "售货员";
testObjc.age = 20;
testObjc.hasJob = true;
testObjc.intro = "还岁月以文明";
try {
FileOutputStream fileOutputStream = new FileOutputStream("/tmp/test.ser");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
///对象序列化
objectOutputStream.writeObject(testObjc);
fileOutputStream.close();
objectOutputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
- 反序列化
public static SerializableTest deserialization() {
SerializableTest test = null;
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream("/tmp/test.ser");
ObjectInputStream inputStream = new ObjectInputStream(fileInputStream);
///反序列化
test = (SerializableTest) inputStream.readObject();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return test;
}
public static void main(String[] args) {
serialization();
SerializableTest obj = deserialization();
if (obj != null) {
System.out.println(obj.name);//波波
System.out.println(obj.job);//售货员
System.out.println(obj.age);//20
System.out.println(obj.hasJob);//true
System.out.println(obj.intro); ///null
}
}
Java多线程
线程状态
创建->就绪(进入就绪队列,待JVM调度)->运行(获取CPU资源)->死亡;
运行状态下,线程可能因为调用sleep
、wait
、suspend
进入阻塞状态。当睡眠时间到或重获CPU资源便会进入就绪状态。主要有三种阻塞:
1.线程运行时调用wait
方法进入等待阻塞。
2.同步阻塞,同步锁:synchronized
。
3.其他阻塞,如:sleep
、join
(等待线程终止或超时或I/O处理完毕)。
线程优先级
Java
中线程的优先级0~10
,如果不设置优先级默认为5
。
线程创建
- 通过实现
Runnable
接口创建线程
创建一个实现Runnable
接口的类,该类需创建一个Thread
实例,运行该Thread
实例的start()
方法,让线程执行。
一个实现了Runnable
的类可以通过实例化一个Thread
实例并将自己作为目标传入,从而在不子类化Thread
的情况下运行。在大多数情况下,如果你只打算覆盖run()
方法而不覆盖其他的Thread
方法,那么就应该使用Runnable
接口。
public class ThreadTest {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName());///main
startNewThread("线程1");
}
public static void startNewThread(String threadName) {
///创建一个实现`Runnable`接口的类
Runnable runnable = new Runnable() {
///实现`run`方法
///获取CPU资源运行任务
@Override
public void run() {
System.out.println(Thread.currentThread().getName());///线程1
try {
///阻塞 5s
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(threadName +"休眠结束");///线程1休眠结束
}
};
///创建线程
Thread t = new Thread(runnable,threadName);
///就绪,加入线程队列,
t.start();
}
}
- 通过继承
Thread
来创建线程
public class ThreadTest extends Thread {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName());///main
ThreadTest customThread = new ThreadTest("线程1");//线程1
customThread.start();
}
public ThreadTest(String name) {
super(name);
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName());///线程1
try {
///阻塞 5s
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this.getName() +"休眠结束");///线程1休眠结束
}
}
- 通过
Callable
和Future
创建线程
public class ThreadTest {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName());///main
///创建一个实现`Callable`接口的类
Callable callable = new Callable() {
@Override
public Integer call() throws Exception {
return 100;
}
};
///使用`FutureTask`包装
FutureTask futureTask = new FutureTask(callable);
//创建线程,并将`FutureTask`作为线程执行对象
Thread thread = new Thread(futureTask);
///使线程处于就绪状态
thread.start();
try {
///取值
Integer integer = futureTask.get();
System.out.println(integer);///100
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}