类
类的源文件名必须与类同名。对于所有的类来说,类名的首字母应该大写。
如果没有显式为一个类指定构造方法,编译器会默认提供。
在创建一个类对象的时候,必须要有构造方法,类的构造方法名必须与类同名,且可以有多个。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<E> extends Iterable<E>
public interface List<E> extends Collection<E>
public abstract class AbstractCollection<E> implements Collection<E>
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E>
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
- ArrayList的关键操作
ArrayList<Integer> 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<Integer> 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<E> extends Iterable<E>
public interface List<E> extends Collection<E>
public abstract class AbstractCollection<E> implements Collection<E>
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E>
///多了一层
public abstract class AbstractSequentialList<E> extends AbstractList<E>
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable
-
LinkedList
的关键方法
///创建
LinkedList<String> 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<E> extends Iterable<E>
public interface Set<E> extends Collection<E>
public abstract class AbstractCollection<E> implements Collection<E>
public abstract class AbstractSet<E> extends AbstractCollection<E> implements Set<E>
public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java.io.Serializable
-
HashSet
关键方法
///创建
HashSet<String> 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<K, V>
public abstract class AbstractMap<K,V> implements Map<K,V>
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable
-
HashMap
的关键方法
///创建
HashMap<Integer,String> map = new HashMap<Integer,String>();
///添加
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<Map.Entry<Integer,String>> 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.泛型方法需要声明类型参数,类型参数声明在函数返回值类型的前面,采用尖括号< >
包裹。形如:<E>
2.类型参数声明可有多个,尖括号内,彼此用逗号,
隔开
3.类型参数能被用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符。
4.泛型方法体的声明和其他方法一样。
5.注意类型参数只能代表引用型类型,不能是原始类型(像 int、double、char 等)。
///示例
public static <T,S> T genericsFunc(T param1, S param2) {
System.out.println(param2);
return param1;
}
泛型类
类名后面添加了类型参数声明部分,类型参数声明部分也包含一个或多个类型参数,参数间用逗号隔开。
public class GenericTest<T,U> {
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<String,String> genericTest = new GenericTest<>("工人","张三");
genericTest.setType("农民");
System.out.println(genericTest.type);
System.out.println(genericTest.user);
}
}
类型通配符与条件限定
类型通配符一般是使用 ?
代替具体的类型参数。例如 List<?>
在逻辑上是 List<String>,List<Integer>
等所有 List<具体类型实参>
的父类。不需要额外声明的类型参数。
public static void printArray(List<?> arr) {
System.out.println(arr.get(0));
}
public static void main(String[] args) {
ArrayList list<String> = new ArrayList<String>();
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<String> list = new ArrayList<String>();
list.add("hello");
//printArray(list);//报错
ArrayList<Integer> numlist = new ArrayList<Integer>();
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<Integer> callable = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
return 100;
}
};
///使用`FutureTask`包装
FutureTask<Integer> futureTask = new FutureTask<Integer>(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();
}
}
}