Java语法基础(一)

类的源文件名必须与类同名。对于所有的类来说,类名的首字母应该大写。

如果没有显式为一个类指定构造方法,编译器会默认提供。

在创建一个类对象的时候,必须要有构造方法,类的构造方法名必须与类同名,且可以有多个。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 : 单一的 16Unicode 字符

引用类型

对象、数组都是引用数据类型。
所有引用类型的默认值都是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

数据类型转换必须满足如下规则:

  1. 不能对boolean类型进行类型转换。
  2. 不能把对象类型转换成不相关类的对象。
  3. 在把容量大的类型转换为容量小的类型时必须使用强制类型转换。
  4. 浮点数到整数的转换是通过舍弃小数得到,而不是四舍五入.

强制类型转换

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中的修饰符有两种:

访问修饰符

用来对类、方法、构造方法、变量的访问设置不同的权限。

  1. public : 范围:所有类可见;修饰对象:类、方法、接口、变量
  2. protected : 范围:同一包中的类和所有子类可见,修饰对象:方法、变量;不能修饰类
  3. private:范围:同一类内可见,修饰对象:方法、变量;不能修饰类
  4. 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 修饰符,用来创建抽象类和抽象方法。如果一个类包含抽象方法,那么该类一定要声明为抽象类。
  • synchronizedvolatile 修饰符,主要用于线程的编程。

synchronized 关键字声明的方法同一时间只能被一个线程访问。synchronized修饰符可以应用于四个访问修饰符。

volatile 修饰的成员变量在每次被线程访问时,都强制从共享内存中重新读取该成员变量的值。而且,当成员变量发生变化时,会强制线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。
一个 volatile 对象引用可能是null

///`synchronized `
public synchronized void showDetails(){
.......
}
///`volatile `
private volatile boolean active;

transient 修饰符:序列化的对象包含被 transient 修饰的实例变量时,java 虚拟机(JVM)跳过该特定的变量。即:用它修饰的变量被序列化不会被持久化。

抽象方法是一种没有任何实现的方法,该方法的具体实现由子类提供。抽象方法不能被声明成 finalstatic。任何继承抽象类的子类必须实现父类的所有抽象方法,除非该子类也是抽象类。
如果一个类包含若干个抽象方法,那么该类必须声明为抽象类。抽象类可以不包含抽象方法。
抽象方法的声明以分号结尾,例如: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对象,那它的值就无法改变了
如果需要改变需要用到StringBufferStringBuilder

///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 中被提出,它和 StringBuffer 之间的最大不同在于 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.html

  • Calendar的创建与使用
    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
    GregorianCalendarCalendar类的一个具体实现。CalendargetInstance()方法返回一个默认用当前的语言环境和时区初始化的GregorianCalendar对象。GregorianCalendar定义了两个字段:ADBC。这是代表公历定义的两个时代。
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中的正则使用主要包括三个类:PatternMatcherPatternSyntaxException

  • 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关键方法:lookingAtmatches
///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" );

lookingAtmatches方法的区别,matches要求整个字符串都要与正则匹配,而lookingAt不要求,但是它要求从字符串的开始位置就要与正则匹配,否则就是false。如果将上述代码:String content = "1waattterwo";,则lookingAt会返回false

  • Matcher关键方法:replaceFirstreplaceAll

replaceFirstreplaceAll方法用来替换正则表达式匹配到的文本。不同的是,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关键方法:appendReplacementappendTail
    Matcher 类也提供了appendReplacementappendTail 方法用于文本替换:
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中的继承

与其他语言差别不大,关键字extendsimplementsfinal

  • 构造方法
    子类是不继承父类的构造函数的。
    如果父类的构造器带有参数,则必须在子类的构造器中显式地通过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修饰的变量。

  1. Java8中可以定义接口的默认方法实现(有方法体),关键字default
  2. 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种子类型ListSetQueue也是接口。具体可实例化的集合类继承了实现上述接口的抽象类。
集合中存储Java对象。常用的可实例化的集合类:ArrayListLinkedListHashSet。常用的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 是无序的,即不会记录插入的顺序。
HashMapkeyvalue 类型可以相同也可以不同,可以是字符串(String)类型的 keyvalue,也可以是整型(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

JavaObject 类是所有类的父类,也就是说 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 等所有 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);
}
  • 限定通配符的类型为特定类型的子类:
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);//报错
    ArrayList numlist = new ArrayList();
    numlist.add(100);
    printArray(numlist);
}
  • 限定通配符的类型为特定类型的父类:
public static void printArray(List arr) {
    System.out.println(arr.get(0));
}

Java序列化

Java序列化,是将一个Java类对象序列化为字节序列的过程,该字节序列会保存对象的数据,也会保存对象类型关联的所有类型的信息;因此该字节序列也可以通过反序列化转化为对象。整个过程基于JVM,因此该字节序列支持平台迁移。

Java的对象支持序列化的条件,该对象所属类型必须实现接口java.io.Serializable

Java 序列化机制的主要类ObjectOutputStreamObjectInputStream

  • 序列化
    当序列化一个对象到文件时, 按照 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资源)->死亡;
运行状态下,线程可能因为调用sleepwaitsuspend进入阻塞状态。当睡眠时间到或重获CPU资源便会进入就绪状态。主要有三种阻塞:
1.线程运行时调用wait方法进入等待阻塞。
2.同步阻塞,同步锁:synchronized
3.其他阻塞,如:sleepjoin(等待线程终止或超时或I/O处理完毕)。

线程优先级

Java中线程的优先级0~10,如果不设置优先级默认为5

线程创建

  1. 通过实现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();
    }
}
  1. 通过继承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休眠结束
    }
}
  1. 通过CallableFuture创建线程
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();
        }
    }
}

你可能感兴趣的:(Java语法基础(一))