教程

狂神说java官网

B站视频链接

基础

注释

//单行注释

/*
多行注释
*/

/**
@Description 文档注释
*/

标识符和关键字

关键字

标识符

以字母、美元符、下划线开头。大小写敏感。

数据类型

类型 大小 备注
byte 1字节
short 2字节
int 4字节
long 8字节 以L结尾
float 4字节 以F结尾
double 8字节
char 2字节 用单引号修饰
boolean 1位 true or false

整数扩展

二进制0b,八进制0,十六进制0x。

浮点数扩展

最好完全避免使用浮点数进行比较。

字符扩展

所有字符的本质还是数字。

Unicode前缀’\u’。

对象,从内存分析。

类型转换

从低到高,byte, short, char -> int -> long -> float -> double。不同数据类型运算,先转换为同一种。

强制类型转换:(类型)变量名。自动类型转换,低到高。

注意:不能对boolean进行转换,不能把对象类型转换为不相干的类型,把高容量转换到低容量的时候是强制转换,转换的时候可能出现精度问题或者内存溢出。

如果你在Java源码中要处理大数字,你可以在数字中加入下划线来提高可读性。使用的时候要注意:在字面常量数字里加下划线是有一定规则的,下划线只能在数字之间,在数字的开始或结束一定不能使用下划线。比如把长整型数字比如10000000000写成一个更具可读性10_000_000_000。

变量、常量、作用域

Java是强类型语言,每个变量都必须声明类型。

声明变量: 数据类型 变量名 = 值;

按作用域分:类变量、实例变量、局部变量。

实例变量从属于对象,如果不自行初始化,就是默认值:0、0.0、false。除了基本类型,其余都是null。

类变量关键字static

常量可以理解为一种特殊变量。声明:final 常量名 = 值;

常量名一般用大写字符。

命名规范

类成员变量、局部变量、方法名:首字母小写和驼峰原则,即除了第一个单词外,后面的单词首字母大写,如lastName、reRun()。

常量:大写字母和下划线,如MAX_VALUE。

类名:首字母大写和驼峰原则,如GoodMan。

运算符

运算符包括算数运算符、赋值运算符、关系运算符、逻辑运算符、位运算符、条件运算符、扩展赋值运算符。

注意除法运算中的变量类型问题。

不存在幂运算符^,幂运算可以使用Math工具类,Math.pow()^表示异或运算。可以用左移位运算实现乘方。

有三目运算符 ? :

包机制

package语句放最前面,import语句在其之后。一般使用公司域名倒置作为包名。

JavaDoc生成文档

JavaDoc可以写在类上面和方法上面。

执行命令javadoc xxx.java即可,也可以使用IDEA的JavaDoc生成功能,在菜单 Tools->Generate JavaDoc项里面。

流程控制

用户交互Scanner

// 基本语法
Scanner s = new Scanner(System.in);
if (scanner.hasNextLine()){
String str = scanner.nextLine();
System.out.println(str);
}
// 凡是属于IO流的类,如果不关闭,会一直占用资源,尽量用完就关掉。
scanner.close();

next():读取到有效字符后才可以结束输入,将有效字符后面的空白作为分隔符或者结束符,不能得到带有空格的字符串。

nextLine():读取输入回车之前的所有字符,可以获得空白。

还有各种类型的nextxxxx()hasNextxxxx()方法。

选择结构

// if选择结构
if (condition1){
statement1;
} else if (condition2){
statement2;
} else{
statement3;
}
// switch选择结构,匹配一个具体的值
// case穿透,记得加break
switch (n){
case "wtf":
System.out.println(1);
break;
case "omg":
System.out.println(2);
break;
default:
System.out.println(3);
}

IDEA可以反编译class文件,如下图。

循环结构

// while循环
while (condition){
statement;
}
// do……while循环
// 保证statement至少执行一次
// while是先判断后执行,dowhile是先执行后判断
do {
statement;
} while ();
// for循环
for (初始值; bool表达式; 迭代){
statement;
}
// 增强for循环
int[] num = {10, 20, 30};
for (int x : num) {
System.out.println(x);
}

break用于强行跳出循环,不再执行剩余语句。

continue用于终止某次循环过程,接着进行下一次循环是否执行的判定。

goto仍是java的一个保留字,但并未在语言中得到正式使用,在带标签的break和continue中有它的影子。

outer:
for (int i = 1; i <= 100; i++) {
for (int j = 2; j <= i; j++) {
if (i % j == 0) {
continue outer; // continue外层循环
}
}
}

方法

何为方法

  • 方法是解决一类问题的步骤的有序组合
  • 方法包含于类或对象中
  • 方法在程序中创建,在其他地方被引用
  • 最好保持方法的原子性,即一个方法只完成一个功能,便于后期扩展
修饰符 返回值类型 方法名(参数类型 参数名){
方法体
return 返回值;
}

调用方法:对象名.方法名(实参列表)

方法重载

在一个类中,有相同的函数名称,但形参不同的函数。

规则:

  • 方法名称必须相同
  • 参数列表必须不同(个数、类型、参数排列顺序不同)
  • 返回类型可同也可不同
  • 仅仅返回类型不同,不足以构成重载

命令行传参

// example
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
for (String arg : args) {
System.out.println(arg);
}

运行java HelloWorld.java this is

# output
this
is

可变参数

在方法声明中,在指定类型后加一个三个点...。一个方法只能有一个可变参数,必须是方法的最后一个参数。

// example
public static int add(int... x) {
int sum = 0;
for (int i : x) {
sum += i;
}
return sum;
}

数组

创建数组

// 声明并创建一个数组
dataType[] arrayRefVa = new dataType[arraySize];
  • 通过索引访问,索引从0开始。
  • 长度固定,一旦创建,不可改变。
  • 可以是任何类型,但不允许混合类型。
  • 数组变量属于引用类型,数组也可以看成是对象,数组中的每个元素相当于该对象的成员变量。
  • 数组本身就是对象,Java中对象是在堆中的,因此数组无论保存原始类型还是其他对象类型,数组对象本身是在堆中的

内存分析

Java内存:

  • 堆:存放new的对象和数组,可以被所有的线程共享,不会存放别的对象引用。
  • 栈:存放基本变量类型(会包含具体数值),引用对象的变量(会存放这个引用在堆里面的具体地址)。
  • 方法区:可以被所有线程共享,包含了所有的class和static变量。

三种初始化

默认初始化:数组是引用类型,它的元素相当于类的实例变量,因此数组一经分配空间,其中的每个元素也被按照实例变量同样的方式被隐式初始化。

多维数组

即数组的数组。

int[][] array = {{1,2},{2,3},{3,4}};

Arrays类

数组工具类java.util.Arrays中的方法都是static修饰的静态方法,在使用的时候可以直接使用类名进行调用。而不用(不是不能)使用对象来调用。看文档了解常用的filltoStringsort等方法。

稀疏数组

当一个数组中大部分为同一值时,可以使用稀疏数组来保存该数组。把具有不同值的元素和行列及值记录在一个小规模的数组中,从而缩小程序的规模。

上图中第一行row表示原矩阵行数,col表示原矩阵列数,value=8表示共有8个有效值,稀疏数组长度为8+1=9。

面向对象

对于描述复杂事物,为了从宏观上把握,从整体上合理分析,我们需要使用面向对象的思路来分析整个系统。但是,具体到微观操作,仍然需要面向过程的思路去处理。

面向对象编程(OOP)本质是以类的方式组织代码,以对象的形式封装数据。

三大特性:封装,继承,多态。

静态方法和非静态方法

static静态方法,和类一起加载的。非静态方法需要类实例化之后才存在,才能使用。

值传递和引用传递

// 引用传递:对象,本质还是值传递。
public class Test {
public static void main(String[] args) {
Person myself = new Person();
System.out.println(myself.name);
changeName(myself);
System.out.println(myself.name);
}

public static void changeName(Person person){
person.name = "Jack";
}
}

class Person{
// 对象的属性
String name;
}
# output
null
Jack

类和对象

类是对某一类事物整体的描述/定义,但是并不能代表某一个具体的事物。

对象是抽象概念的具体实例。

创建的时候,使用new关键字,会分配内存空间,还会给创建好的对象进行默认的初始化以及对类中构造器的调用。

一个项目应该只有一个main方法。

构造器

类中的构造器又称构造方法,必须和类的名字同名,必须没有返回类型,也不能写void

class Person {
String name;

public Person(String name) {
this.name = name;
}

public void study() {
System.out.println(this.name + " is studying.");
}
}
// example
Person myself = new Person("lfalive");
System.out.println(myself.name);
myself.study();
# output
lfalive
lfalive is studying.

定义有参构造之后,如果想使用无参构造,显式地定义一个无参的构造。

内存分析

封装

高内聚,低耦合:内部数据操作细节自己完成,仅暴露少量的方法给外部使用。

封装(数据的隐藏):通常应禁止直接访问一个对象中数据的实际表示,而应通过操作接口来访问、

属性私有:private关键字。通常提供一些可以操作这个属性的方法,例如public的get、set方法。

class Person {
private String name;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}
// example
Person myself = new Person();
myself.setName("lfalive");
System.out.println(myself.getName());
# output
lfalive

总结:属性私有,get/set。

属性级别:publicprotecteddefaultprivate

继承

继承是类和类之间的一种关系。子类继承父类(包括父类的所有方法),使用关键字extend来表示。

在Java中,所有类都默认直接或者间接继承Object。

Java中只有单继承,没有多继承。

// example
// 继承了上例中的Person类,可以使用getName和setName方法
class Student extends Person {
}

使用super调用父类的属性和方法。私有的东西无法被继承,即super不能调用父类的private方法。

new一个子类对象时,会先执行super(),调用父类的无参构造。即super()方法是调用父类的构造器,必须要在子类构造器的第一行。默认调用的是父类的无参构造,如果父类没有无参构造,就会报错。superthis不能同时调用构造方法。

多态

重写

重写都是方法的重写,和属性无关。

父类的引用可以指向子类。例如Person you = new Student();

静态方法是类的方法,非静态方法是对象的方法。对于上例而言,因为youStudentnew出来的对象,因此在有static时,you调用了Person类的方法,没有static时,you调用的是对象的方法,即Student的方法。

public class HelloWorld {
public static void main(String[] args) {
Student you = new Student();
you.test();
Person myself = new Student();
myself.test();
}
}

class Person {
public void test() {
System.out.println("person test");
}
}

class Student extends Person {
@Override // 子类重写了父类的方法,和非静态方法有关,private方法不能重写。
public void test() {
System.out.println("student test");
}
}

重写注意事项

  1. 重写的方法可以使用 @Override 注解来标识。
  2. 构造方法不能被重写。
  3. 声明为 final 的方法不能被重写。
  4. 子类和父类在同一个包中时,子类可以重写父类除了声明为 privatefinal 方法的其他方法。
  5. 子类和父类不在同一个包中时,子类只能重写父类的声明为 publicprotected 的非 final 方法。
  6. 如果不能继承一个方法,则不能重写这个方法。重写是在继承的基础上,如果方法无法被继承那么就无法重写
  7. 方法名、参数列表必须相同
  8. 修饰符:范围可以扩大,不能缩小:public>protected>default>private
  9. 抛出的异常:范围可以被缩小,不能被扩大:ClassNotFound –> Exception

为什么需要重写:父类的功能,子类不一定需要,或者不一定满足。

instanceof和类型转换

对象能执行哪些方法,主要看对象左边的类型,和右边关系不大。

子类能调用的方法,都是自己的或者继承父类的。父类不能调用子类独有的方法。

多态注意事项

  • 多态是方法的多态,属性没有多态
  • 父类和子类有联系,类型转换异常为ClassCastException
  • 存在条件:继承关系,方法需要重写(but有些方法不能被重写,见上文),父类引用指向子类对象Father f1 = new Son();

instanceof判断一个对象是否是特定类的一个实例。

// example
public class HelloWorld {
public static void main(String[] args) {
// Object > String
// Object > Person > Teacher
// Object > Person > Student
Object s1 = new Student();
System.out.println(s1 instanceof Student);
System.out.println(s1 instanceof Person);
System.out.println(s1 instanceof Object);
System.out.println(s1 instanceof Teacher);
System.out.println(s1 instanceof String);
System.out.println("=================================");
Person s2 = new Student();
System.out.println(s2 instanceof Student);
System.out.println(s2 instanceof Person);
System.out.println(s2 instanceof Object);
System.out.println(s2 instanceof Teacher);
// System.out.println(s2 instanceof String); // 编译报错
System.out.println("=================================");
Student s3 = new Student();
System.out.println(s3 instanceof Student);
System.out.println(s3 instanceof Person);
System.out.println(s3 instanceof Object);
// System.out.println(s3 instanceof Teacher); // 编译报错
// System.out.println(s3 instanceof String); // 编译报错
}
}
class Person {
}
class Student extends Person {
}
class Teacher extends Person {
}
# output
true
true
true
false
false
=================================
true
true
true
false
=================================
true
true
true

System.out.println(X instanceof Y);能不能编译通过,就看X和Y之间有没有父子关系。

类型之间的转换:父对象可以使用括号强制转换为子对象。子类转换为父类不用强制转换,但可能丢失一些自己本来的方法。可以方便方法的调用,减少重复的代码!

static关键字详解

静态代码块

// example
public class Student extends Person {
public Student() {
System.out.println("Student无参构造");
}

{
System.out.println("匿名代码块");
}

static {
System.out.println("静态代码块");
}

public static void main(String[] args) {
Student s1 = new Student();
System.out.println("==================");
Student s2 = new Student();
}
}
# output
静态代码块
Person无参构造
匿名代码块
Student无参构造
==================
Person无参构造
匿名代码块
Student无参构造

可见,执行顺序是静态代码块、父类构造方法、匿名代码块,子类构造方法。且静态代码块只最初执行一次。

静态导入包

import static java.lang.Math.random;

public class Student extends Person {
static {
System.out.println(random());
}

public static void main(String[] args) {
}
}

如此,使用random方法时,就不用写Math.random(),而是可以直接写random()

被final修饰的类不能再有子类。

abstract抽象类

抽象方法:只有方法名字,没有方法的实现。

抽象类的所有方法,继承了它的子类,都必须要实现它的方法,除非子类也是抽象类。

特点:不能new这个抽象类,只能靠子类去实现,它是一种约束!一旦类里面有抽象方法,则类必须是抽象类。抽象类里面可以写普通方法。

存在的意义:抽象出来,提高开发效率,提高可扩展性。

interface接口

普通类只有具体实现,抽象类有具体实现和规范(抽象方法),接口则只有规范,自己无法写方法。

接口的本质是契约。接口中的所有定义其实都是抽象的public abstract。所有变量其实都是public static final

类可以实现接口,关键字implements。类实现了接口,就必须重写接口中的方法。

作用:约束;定义一些方法让不同的人实现。

接口不能被实例化,接口中没有构造方法。

extends只有单继承,但是可以implements可以多继承。

// UserService.java
public interface UserService {
void add(String name);

void delete(String name);

void update(String name);

void query(String name);
}
// TimeService.java
public interface TimeService {
void timer();
}
// UserServiceImpl.java
public class UserServiceImpl implements UserService, TimeService {
@Override
void add(String name) {

}

@Override
public void delete(String name) {

}

@Override
public void update(String name) {

}

@Override
public void query(String name) {

}

@Override
public void timer() {

}
}

内部类

  1. 成员内部类
  2. 静态内部类
  3. 局部内部类
  4. 匿名内部类
public class Outer {
private int id = 11;

public void out() {
System.out.println("Outer");
}

// 成员内部类
public class Inner1 {
public void in() {
System.out.println("Inner");
}

public void GetID() {
System.out.println(id);
}
}

// 方法内部类(局部内部类)
public void method() {
class Inner2 {
}
}
}

// 一个java文件中可以有多个class,但是只能有一个public class。
class A {

}
// 测试成员内部类
public class Application {
public static void main(String[] args) {
Outer outer = new Outer();
Outer.Inner1 inner = outer.new Inner1();
inner.in();
}
}

异常

软件程序在运行过程中,经常遇到各种异常或意外。这些Exception,让我们写的程序做出合理的处理,而不至于程序崩溃。

  • 检查性异常:最具代表性的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如要打开一个不存在的文件时。这些异常在编译时不能被简单忽略。
  • 运行时异常:可能被程序员避免的异常。这些异常在编译时可以被忽略。
  • 错误ERROR:错误不是异常,而是脱离程序员控制的问题。例如栈溢出。

Java把异常当作对象来处理,定义基类java.lang.Throwable作为所有异常的超类。分为两大类:错误Error异常Exception。出现前者时,JVM一般终止线程;后者则是通常情况下可以被程序处理,并且在程序中应该尽可能的去处理。

异常处理

五个关键字trycatchfinallythrowthrows

// example
public static void main(String[] args) {
int a = 1;
int b = 0;
try {//监控区域
System.out.println(a / b);
} catch (ArithmeticException e) {//捕获异常
System.out.println("捕获");
} finally {//处理善后工作
System.out.println("finally.");
}
}

catch里面的参数是想要捕获的异常类型,catch可以写多个,层层递进,范围最大的在最后。

public class Demo01 {
public static void main(String[] args) {
try {
new Demo01().test(1, 0);
} catch (ArithmeticException e) {
e.printStackTrace();
}
}

// 假设这个方法中处理不了这个异常,方法上抛出异常。
public void test(int a, int b) throws ArithmeticException {
if (b == 0) {
throw new ArithmeticException(); //主动抛出异常,一般在方法中使用
}
}
}

自定义异常

用户自定义异常类,只需继承Exception类即可。

public class MyException extends Exception {
private int detail;

public MyException(int a) {
this.detail = a;
}

// 异常的打印信息
@Override
public String toString() {
return "MyException{" + detail + '}';
}
}
// test
public class Test {
public static void main(String[] args) {
try {
test(11);
} catch (MyException e) {
System.out.println(e);
}
}

static void test(int a) throws MyException {
System.out.println("传递的参数为" + a);
if (a > 10) {
throw new MyException(a);
}
System.out.println("OK");
}
}
# output
# when test(11)
传递的参数为11
MyException{11}

# when test(9)
传递的参数为9
OK

经验总结:

  • 采用逻辑去合理规避同时辅助try-catch处理。
  • 多重catch后面,加一个catch(Exception)来处理可能被遗漏的异常。
  • 不确定的代码,也可以加上try-catch来处理潜在异常。
  • 尽量去处理异常,切忌只是简单的printStackTrace去打印输出。
  • 具体如何处理,根据业务需求去决定。
  • 尽量添加finally语句块去释放占用的资源。

其它

IDEA快捷操作

  • 输入一个数字,再输入.,即可快捷生成for循环语句。输入fori也可以。
  • Ctrl+D可以将当前行复制到下一行。
  • 类中,Alt+Insert快捷生成构造器,以及Getter和Setter,以及重写方法。或者鼠标右键,选择Generate。
  • 选中多行代码后,Ctrl+/为快捷注释,Tab为整体缩进右移,Tab+Shift为整体缩进左移。
  • 光标移动到某个类,Ctrl+H快捷键查看其继承关系。或点击侧边栏Hierarchy。
  • 选中某部分代码,Ctrl+Alt+T可以使选中代码surround with,生成while、if、try等。好像和QQ冲突了,可以改快捷键。

递归小心爆栈。

《阿里巴巴Java开发手册》