0%

Java 基础入门 阶段1 & 阶段2

JAVA程序执行过程

  • 编写Hello.java类,
1
2
3
4
5
public class Hello {  // 定义类
public static void main(String[] args) { // 主函数
System.out.println("Hello world"); // 输出
}
}
  • 通过编译生成Hello.class文件,(字节码文件)
1
javac Hello.java
  • 调用JVM(java 虚拟机)运行生成结果
1
java Hello  # 实际在运行Hello.class,但是不写后缀

JAVA类编写注意事项

  • 一个源文件中最多只能有一个public 类,其他类的个数不限。
  • 如果一个文件中包含public 类,则文件名必须和该类同名。

JAVA转义字符

  • JAVA中常用的转义字符:
换行符 含义
\n 换行符
\t 制表符,实现对齐
\r 一个回车

JAVA注释

  • 单行注释
1
// 这是单行注释
  • 多行注释

多行注释不允许嵌套多行注释

1
2
3
4
/*
这是多行注释
多行注释
*/
  • 文档注释

里面是java doc标签

1
2
3
4
/**
* @author 177411
* @version 1.0
*/

JAVA变量

变量是程序的基本组成单位,有三要素(类型 + 名称 + 值),变量表示内存中的一个存储区域。

1
2
3
4
5
6
7
8
public class Variable {
public static void main(String[] args) {
int a; // 声明变量
a = 1; // 变量赋值
int b = 2; // 声明赋值合并
System.out.println(a);
}
}

变量类型

  • int 整型,占4字节
  • double 双精度浮点类型,占8字节
  • char 字符型,占2字节
  • String 字符串类型,

JAVA数据类型 ⭐

java 中定义数据必须指定明确的数据类型,可以看出java 是强类型语言

基本数据类型

  • 数值型
    • 整数类型,存放整数byte[1], short[2], int[4], long[8]
    • 浮点(小数)类型float[4], double[8]
  • 字符型char[2],存放单个字符
  • 布尔型boolean[1]

整数类型

类型不同带来的是占用存储空间不同,进而带来的是数值范围不同。1个字节等于8位。

1
2
3
4
5
6
7
public class IntDetail {
public static void main(String[] args) {
int n1 = 1; // 4字节
long n2 = 1L; // 8字节
float n3 = 1F;
}
}

浮点数类型

  • 浮点数 = 符号位 + 指数位 + 尾数位
  • 尾数部分可能丢失,造成精度损失(小数都是近似值)

陷阱:

2.78.1 / 3 不相等,8.1 / 3 是接近2.7的小数,而不是2.7。所以需要判断时,应该是以两个数的差值的绝对值,在某个精度范围内进行判断。

字符类型

  • 必须用单引号''
  • 允许使用转义字符\
  • 允许进行运算 'a' - 'a'
  • 字符型存储到计算机中,需要将对应的码值(整数)找出来进行存储。
  • 字符和整数的对应关系是通过字符编码表决定的,是规定好的。

基本数据类型的转换

自动类型转换

  • 精度小的类型自动转换为精度大的数据类型。
  • char < int < long < float < double
  • byte < short < int < long < float < double

注意:

  1. 有多种类型的数据类型进行混合运算时,系统首先将所有类型转为容量大的类型,然后再进行计算。
  2. 把容量大的数据类型赋值给容量小的数据类型时,会报错,反之则会进行自动类型转换。
  3. (byte, short)char 之间不会自动转换。

强制类型转换

将容量大的类型转换为容量小的数据类型。使用时需要加上强制转换符(),但可能造成精度降低或溢出。

1
2
3
4
5
6
public class ForceConvert {
public static void main(String[] args) {
int a = (int) 1.9;
System.out.println(a);
}
}

基本数据类型和String类型的转换

  • 基本类型转String类型:基本类型值 + ""
  • String类型转基本类型:通过基本数据类型的包装类调用parseXXX进行转换。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class StringToBasic {
public static void main(String[] args) {
String str = "123";
int a = Integer.parseInt(str);

str = "89.0";

double b = Double.parseDouble(str);

float c = Float.parseFloat(str);

short d = Short.parseShort(str);

long e = Long.parseLong(str);

boolean fl = Boolean.parseBoolean("true");
}
}

注意事项:

  • 在将String类型转换为基本数据类型时,需要确保String类型能够转换为有效的数据。如果格式不正确,则会抛出异常,程序就会终止。

引用数据类型

  • class,例如String
  • 接口interface
  • 数组[]

JAVA API文档

中文在线文档

JDK > Packages > Interfaces + class + Exceptions > function

JAVA 算数运算符

运算符是一种特殊的符号,用于表示数据的运算,赋值和比较。

算数运算符

运算符 运算
+ 正号
- 负号
+
-
*
/
% 取模(取余)
++ 自增
自减
+ 字符串相加

加号

  • 当左右两边都是数值类型,则做加法。
  • 当左右两边有一方是字符串类型,则做拼接。
  • 运算顺序,从左到右,右括号除外。

赋值运算符

基本赋值运算符:=

复合运算符:+=-=*=/=%=

关系运算符

关系运算符的结果都是boolean 类型

运算符 运算
== 相等于
!= 不等于
< 小于
> 大于
<= 小于等于
>= 大于等于
instanceof 检查是否是类的对象

逻辑运算符

用于连接多个条件(多个关系表达式),最终的结果也是一个boolean

a b a&b a&&b a\ b a\ \ b !a a^b(异或)
T T T T T T F F
T F F F T T F T
F T F F T T T T
F F F F F F T F

&&和&的区别

  • &&当第一个条件为false时,不再判断第二个条件。效率高。
  • &当第一个条件为false时,仍需判断第二个条件。效率低。
  • 同理 ||| 的区别也是如此

位运算符

运算符 运算
& 按位与
\ 按位或
~ 按位取反
^ 按位异或
>> 算数右移,地位溢出,符号位不变,并用符号位补溢出的高位
<< 算数左移,符号位不变,低位补
>>> 逻辑右移

三元运算符

基本语法:条件表达式 ? 表达式1 : 表达式2

  • 如果条件表达式结果为true,运算后的结果是表达式1;
  • 如果条件表达式结果为false,运算后的结果是表达式2;

标识符

命名规则:(必须遵守)

  • 由26个英文字母大小写,0-9,_或$组成
  • 数字不可以开头,
  • 不可以使用关键字或保留字,但能包含关键字或保留字
  • 严格区分大小写,长度无限制。
  • 不能含有空格。

命名规范

  • 包名:多个单词组成时所有字母都是小写。
  • 类名、接口名:多单词组成时,所有单词的首字母大写。
  • 变量名、方法名:多单词组成时,第一个单词首字母小写,第二个单词开始后的所有单词首字母大写。
  • 常量名:所有字母都大写。多单词时,每个单词用下划线连接。

键盘输入语句

导入java.util.Scanner包,创建Scanner对象,调用方法进行输入。

1
2
3
4
5
6
7
8
9
10
11
12
import java.util.Scanner;

public class Input {
public static void main(String[] args) {
Scanner myScanner = new Scanner(System.in); // 创建Scanner 对象
System.out.println("请输入名字");
String name = myScanner.next(); // 接收用户的输入
int age = myScanner.nextInt();
System.out.println(name);
System.out.println(age);
}
}

进制

  • 二进制:以0b0B开头
  • 十进制:
  • 八进制:以数字0开头
  • 十六进制:以0x0X开头表示。

进制的转换

其他进制转十进制

  • 二进制转十进制

从最低位(右边)开始,将每个位上的数提取出来,乘以2的(位数-1)次方,然后求和。

  • 八进制转十进制

从最低位(右边)开始,将每个位上的数提取出来,乘以8的(位数-1)次方,然后求和。

  • 十六进制转十进制

从最低位(右边)开始,将每个位上的数提取出来,乘以16的(位数-1)次方,然后求和。

十进制转其他进制

  • 十进制转二进制

将该数不断除以2,指导商为0为止,然后将每步得到的余数倒过来。就是对应的二进制。

  • 十进制转八进制

将该数不断除以8,指导商为0为止,然后将每步得到的余数倒过来。就是对应的八进制。

  • 十进制转十六进制

将该数不断除以16,指导商为0为止,然后将每步得到的余数倒过来。就是对应的十六进制。

其他进制相互转换

  • 二进制转八进制

从低位开始,将二进制数每三位一组,转成对应的八进制数即可。

  • 二进制转十六进制

从低位开始,将二进制数每四位一组,转成对应的十六进制数即可。

  • 八进制转二进制

将八进制数的每一位转成对应的一个3位的二进制数即可。

  • 十六进制转二进制

将十六进制数的每一位转成对应的一个4位的二进制数即可。

控制结构

顺序控制

程序从上到下逐行执行,中间没有任何判断和跳转。

分支控制

  • if...else if...if...
  • switch...case...default...

循环控制

  • for(循环变量初始化; 循环条件; 循环变量迭代) { 循环体 }
  • while(循环条件) { 循环体; 循环变量迭代}
  • do{ 循环体; 循环变量迭代} while(循环条件)

跳转控制

  • break:结束最近的一层循环,推出该层循环
  • continue:结束本次循环,继续下一层循环
  • return:表示跳出所在方法

数组、排序、查找

数组

数组可以存放多个 同一类型 数据,数组也是一种数据类型,是引用类型。

数组下标是从0开始编号的。

数组的定义

  • 动态初始化:数组类型 数组名[] = new 数据类型[大小]

  • 动态初始化,先声明,在创建。

  • 静态初始化,数据类型 数组名[] = {元素值, 元素值, ...}
1
2
3
4
5
6
7
8
public class ArrayDef {
public static void main(String[] args) {
int arr[] = new int[3]; // 动态初始化数组
int num[];
num = new int[3]; // 先声明,再分配
int q[] = {1, 2, 3}; // 静态初始化数组
}
}

数组的注意事项

  • 数组内的元素必须是相同类型,
  • 数组中的元素类型可以是任何数据类型,包括基本数据类型或引用数据类型,但不能混。
  • 数组创建后,没有赋值,有默认值。
  • 数组下标是从0开始的。
  • 数组下标必须在指定范围内使用。
  • 数组属于引用类型,数组型数据是对象(object)。

数组的赋值机制

  • 基本数据类型赋值,这个值就是具体的数据,而且相互不影响。
  • 数组在默认情况下,是阴影传递,赋的值是地址。
1
2
3
4
5
6
public class ArrayDef {
public static void main(String[] args) {
int arr[] = {1, 2, 3};
int num[] = arr; // 引用赋值,此时num发生变化,则arr会发生变化。
}
}

数组拷贝

1
2
3
4
5
6
7
8
9
public class ArrayDef {
public static void main(String[] args) {
int arr[] = {1, 2, 3};
int arr2 = new int[arr.length]; // 数组拷贝
for(int i = 0; i < arr2.length; i ++){
arr2[i] = arr[i];
}
}
}

排序

冒泡排序

每次冒泡都将最大的数找出,并移动到后面。

  • 需要进行arr.length - 1 轮排序。
  • i轮排序需要确定第i大数的位置。
  • 每轮比较依次减少i次比较。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class BubbleSort {
public static void main(String[] args) {
int arr[] = {26, 90, 86, 45, 13, 72, 16};
for(int i = i; i < arr.length; i ++) { // 需要进行 arr.length - 1 次
// 每次需要比较 arr.length - i - 1次
for (int j = 0; j < arr.length - i; j++) {
if (arr[j] > arr[j + 1]) { // 如果当前数比后一个数大,则进行交换
int a = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = a;
}
}
}
}
}

查找

  • 顺序查找:用目标值依次和数组中的元素进行比较,若相等,则查找到,若遍历结束仍没有找到,则不存在。
  • 二分查找,需要保证原数组是有序的。

二维数组

  • 动态初始化:类型 数组名[][] = new 类型[大小][大小]
  • 动态初始化:先声明,在分配空间
  • 动态初始化:列数不确定
  • 静态初始化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class ArrayDef {
public static void main(String[] args) {
int arr[][] = new int[3][4]; // 动态初始化数组

int num[][];
num = new int[3][3]; // 先声明,再分配

int arr[][] = new int[3][]; // 列数不确定
for(int i = 0; i < arr.length; i ++){ // 给列分配空间。
arr[i] = new int[i + 1];
}

int q[][] = {{1, 2, 3}, {1, 1, 1}, {2, 2, 3}}; // 静态初始化数组
}
}

类与对象

  • 类:就是数据类型,例如Cat
  • 对象:就是一个具体的实例,对象[属性, 行为]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package object;

public class Object01 {
public static void main(String[] args) {
cat cat1 = new cat(); // 对象
cat1.name = "hhh";
cat1.age = 20;
cat1.color = "红色";
int age = cat1.getAge();
}
}

class cat{ // 类
// 属性
String name;
int age;
String color;

//行为
int getAge(){
return this.age;
}
}

属性

属性也叫成员变量,也叫field(字段)

  • 属性是类的一个组成部分,一般是数据类型,也可以是引用类型。

属性的定义方法:

访问修饰符 属性类型 属性名

访问修饰符包括:publicprivateprotected,默认为public

成员方法

某个类中的函数就是该类的成员方法或者叫成员函数。

成员方法的定义:

访问修饰符 返回类型 方法名(形参列表) { 函数体 ; return 返回值;}

  • 形参列表:代表成员方法的输入。
  • 返回类型:表示成员方法的输出,若为void,则没有返回值
  • return不是必须的

传参机制

  • 值传递;方法运行后不会影响实参的值。
1
2
3
4
5
6
7
class Test{
public void swap(int a, int b){
int c = a;
a = b;
b = c;
}
}
  • 引用数据传递:接收数组,传递的是地址。

方法的递归调用

  • 直接递归:方法自己本身直接调用自己。
  • 简介递归

方法重载

JAVA允许在同一个类中的成员方法中,有多个重名函数存在,但是要求形参列表不一样!

重载的优点

  • 减轻了起名的麻烦
  • 减轻了记名的麻烦
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class OverLoad01 {
public static void main(String[] args) {
MyCaculate myCaculate = new MyCaculate();
int caculate = myCaculate.Caculate(1, 2);
double caculate1 = myCaculate.Caculate(1, 2.2);
int caculate2 = myCaculate.Caculate(1, 2, 3);
}
}

class MyCaculate{
public int Caculate(int a, int b){
return a + b;
}
public double Caculate(int a, double b){
return a + b;
}
public int Caculate(int a, int b, int c){
return a + b + c;
}
}

注意事项

  • 方法名必须一样
  • 形参列表必须不同,形参类型不同 或者 个数不同 或者 顺序不同。
  • 参数名没有要求
  • 返回类型无要求,返回类型不是构成方法重载的条件。

可变参数

JAVA允许将同一个类中多个同名同功能但参数个数不同的方法,封装成同一个方法。

for(参数类型... 参数名),此时参数名可以当做数组使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class VarParameter01 {
public static void main(String[] args) {
HspMethod hspMethod=new HspMethod();
int sum = hspMethod.sum(1, 2, 4);
int sum1 = hspMethod.sum(1, 2);
}
}

class HspMethod {
int sum(int... nums){ // 可以接受int类型的多个参数,可以将nums当作数组使用
int cun = 0;
for(int i = 0; i < nums.length; i ++){
cun += nums[i];
}
return cun;
}
}

注意事项

  • 可变参数的实参可以直接是数组
  • 可变参数的实参可以是0个或多个
  • 可变参数可以和普通类型的参数一起放在形参列表,但必须保证可变参数在最后
  • 一个形参列表只能出现一个可变参数

作用域

包括局部变量或者全局变量

  • 局部变量:定义在某一个成员方法中的变量 或定义在某一个代码块中 。作用域仅在该成员方法中 或 该代码块中。
  • 全局变量:直接定义在类中的变量,属性就是全局变量,在该类中的任何成员方法中都可以直接使用。作用域在整个类中。
  • 全局变量可以不用赋值,有默认值,局部变量必须赋值后才可以使用,不会有默认值。

注意事项

  • 属性和局部变量可以重名,使用时遵守就近原则
  • 在同一个作用域内,两个局部变量不能重名
  • 属性声明周期比较长,局部变量声明周期相对比较短

作用域范围

  • 属性:可以被本类所有成员方法使用,也可以通过对象调用被其他类使用。
  • 局部变量:只能在本成员方法中使用。

修饰符

  • 属性:可以添加修饰符
  • 局部变量:不可以添加修饰符

构造方法/构造器

基本语法:[修饰符] 方法名(形参列表) { 方法体; }

作用:在创建对象时,实现对对象的初始化。

  • 构造器的修饰符可以是默认的也可以是其他的。
  • 构造器没有返回值
  • 方法名和类名必须保持一致
  • 构造器的调用是由系统完成的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Constructor {

public static void main(String[] args) {
Person person = new Person("xzt", 22);
System.out.println(person.name + "的年龄为" + person.age);
}
}

class Person {
int age;
String name;

Person() { // 默认构造函数

}

Person(String name, int age) { // 重写构造函数
this.age = age;
this.name = name;
}
}

注意事项

  • 一个类可以构造多个构造器,即构造器的重载
  • 若没有自定义构造器,则系统会自动给类生成一个默认无参构造器,但是若定义了一个新的构造器,则不会再自动生成一个默认无参构造器。

对象创建流程

  • 加载类信息,只会加载一次
  • 在堆中分配空间(地址)
  • 完成对象初始化,
    • 默认初始化
    • 显式初始化
    • 构造器初始化
  • 在对象堆中的地址,返回给创建的对象

this关键字

java虚拟机给每个对象分配this,代表当前对象。

作用:在成员方法中,this主要用在当形参和属性名同名时,使用this.属性名代替属性名,来区分同名问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Constructor {

public static void main(String[] args) {
Person person = new Person("xzt", 22);
System.out.println(person.name + "的年龄为" + person.age);
}
}

class Person {
int age;
String name;

Person() { // 默认构造函数

}

Person(String name, int age) { // 重写构造函数
this.age = age; // 此时使用this.age 代替属性age
this.name = name;
}
}

本质:哪个对象调用,this就代表哪个对象

注意事项

  • this可以访问本类的属性、方法、构造器
  • this用于区分当前类的属性和局部变量
  • 访问成员方法时语法:this.方法名(参数列表)
  • 访问构造器语法:this(参数列表);注意只能在构造器中使用
  • this不能再类定义的外部使用,只能再类定义的方法中使用

Intellij IDEA

快捷键

功能描述 快捷键
删除当前行 ctrl + y
复制当前行 ctrl + d
代码格式化 ctrl + alt + L
补全代码 补全代码:alt + /
单行注释 ctrl + /
多行注释 ctrl + shift + /
显示错误 alt + Enter
快速运行代码 ctrl + shift + F10
生成构造器、get函数、set函数 alt + insert
查看类的继承关系 ctrl + h
定位到定义的方法 ctrl + b
自动分配变量名 在后面加.var

模板

查看模板:file -> settings -> editor -> Live templates -> Java,可以自定义自己的模板

功能描述 快捷键
主函数 main + enter
输出语句 sout + enter
for循环语句 fori + enter

面向对象编程(中)

作用

  • 可以区分相同名字的类
  • 当类很多时,可以很好的管理类
  • 控制访问范围

基本语法package.com.constructor

  • package 关键字,表示打包
  • com.constructor 表示包名

原理: 创建不同的文件夹来保存类文件。

命名规则

  • 只能包含数字、字母、下划线、小圆点,但不能以数字开头,不能是数字或保留字

命名规范

  • 一般是小写字母 + 小圆点,一般是com.公司名.项目名.业务模块名

常用的包

  • java.lang.* 默认引入,不需要再引入。例如Mathl类
  • java.util.* 系统提供的工具包,工具类,使用Scanner
  • java.net.* 网路包,网络开发
  • java.awt.* Java界面开发,GUI

导入包

基本语法:import 包,引入一个包的目的是使用该包下面的类。

  • package的作用是声明当前类所在的包,需要放在类的最上面,一个类中最多只有一个package
  • import 放在package和类之间,可以有多条且顺序没有要求
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.pkg;

import java.util.Scanner; // 只会导入java.util下面的Scanner类
import java.util.*; // 导入java.util包下面的所有类, 不建议使用
import java.util.Arrays;

public class Import01{
public static void main(String[] args){
int[] arr = {-1, 20, 2, 13, 3};

Arrays.sort(arr); // 排序
for(int i = 0; i < arr.length; i ++){
System.out.println(arr[i]);
}
}
}

访问修饰符

java提供四种访问修饰控制符,用于控制方法和属性(成员变量)的访问权限。

  • 公开级别:public 修饰,对外公开
  • 受保护级别:protected 修饰,对子类和同一个包中的类公开
  • 默认级别:没有修饰符,向同一个包的类公开,对子类不能访问
  • 私有级别:private 修饰,只有类本身可以访问,不对外公开

注意事项

  • 修饰符可以用来修饰类中的属性,成员方法以及类
  • 只有默认的和public才可以修饰类

三大特征 ⭐

封装

定义: 把抽象出来数据[属性]和对数据的操作[方法]封装在一起,数据被保护在内部,程序的其他部分只有通过被授权的操作[方法],才能对数据进行操作。

优点

  • 隐藏实现细节,直接调用即可
  • 可以对数据进行验证,保证安全合理

步骤

  1. 将属性私有化
  2. 提供一个public 的 set方法,对属性判断并赋值
  3. 提供一个public 的 get方法,用于获得属性值

当使用构造器对属性赋值时,构造器函数体的编写方法需要进行改变

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
package com.xzt.encapsulation;

public class Encapsulation {
public static void main(String[] args) {
Person person = new Person();
person.setName("xzt");
person.setAge(200);
person.setSalary(30000);
System.out.println(person.info());

Person smith = new Person("smith", 30, 15000);
}
}

class Person {
public String name;
private int age;
private double salary;

// 默认构造器
public Person() {
}

// 有三个属性的构造器 并进行重写
public Person(String name, int age, double salary) {
setName(name);
setAge(age);
setSalary(salary);
}

// 快捷键 alt + insert
public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
if(age >= 1 && age <= 120)
this.age = age;
else{
System.out.println("年龄需要在1~120之间");
this.age = 18;
}
}

public double getSalary() {
return salary;
}

public void setSalary(double salary) {
this.salary = salary;
}

public String info() {
return "信息为:名字是 " + getName() + ", 年龄是:" + getAge() + ",工资是:" + getSalary();
}
}

继承

定义: 当两个类中有很多属性和方法是相同的,则可以使用继承来减少代码复用。

基本语法:

class 子类名 extends 父类名 {}

  • 子类会自动拥有父类定义的属性和方法
  • 父类又叫超类或者基类
  • 子类又叫派生类

优点: 代码的复用性、扩展性、维护性都得到很大提升

注意事项:

  • 子类继承了所有的属性和方法,但是私有属性不能在子类中直接访问,要通过公共的方法去访问。
  • 子类必须调用父类的构造器,完成父类的初始化。 在子类的构造函数中会默认调用super()函数
  • 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会调用父类的无参构造器,如果父类中没有提供无参构造器,则必须在子类的构造器中用super()去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过。
  • 如果希望指定去调用父类的某个构造器,则显示的调用一下,super(参数列表)
  • super() 在使用时,需要放在构造器的第一行(super()只能在构造器中使用)
  • super()this() 都只能放在构造器的第一行,因此这两个方法不能共存在一个构造器
  • java所有类时Object类的子类,Object类是所有类的基类
  • 父类构造器的调用不限于直接父类。将一直往上追溯直到Object类(顶级父类)
  • 子类最多只能继承一个父类(指直接继承),即java中是单继承机制
  • 不能滥用继承,子类和父类之间必须满足is-a的逻辑关系(子类是父类中的一类)

super关键字: super() 代表父类的引用,用于访问父类的属性,方法,构造器。

  • 访问父类的属性/方法,但不能访问private私有属性/方法,super.属性名
  • 访问父类的构造器,super(参数列表)只能放在构造器的第一行

优点

  • 调用父类构造器,分工明确,父类属性由父类初始化,子类的属性由子类初始化
  • 当子类中有和父类中成员(属性和方法)重名时,为了访问父类的成员,必须通过super关键字。
  • 若多个上级类都有同名,则super访问遵循就近原则

superthis的比较

No. 区别点 this super
1 访问属性 访问本类中的属性,若本类中没有此属性,则从父类中继续查找 访问父类中的属性
2 调查方法 访问本类中的方法,若本类中没有此方法,则从父类中继续查找 访问父类中的方法
3 调用构造器 调用本类的构造器,必须放在构造器首行 调用父类的构造器,必须放在子类构造器的首行
4 特殊 表示当前对象 子类中访问父类对象

方法重写/覆盖

方法重写就是子类有一个方法,和父类的某个方法的名称、返回类型、参数一样,那么就说子类的这个方法覆盖了父类的方法。

注意事项

  • 子类的方法的参数、方法名称要和父类方法的参数、名称完全一样
  • 子类的方法的返回类型要和父类方法的返回类型一样,或者是父类返回类型的子类。例如:父类返回类型为Object,子类方法的返回类型可以是String
  • 子类方法不能缩小父类方法的访问权限:public > protected > 默认 > private

多态

方法或对象具有多种形态。是面向对象的第三大特征,多态是建立在封装和继承的基础之上的。

作用: 提高代码的复用性

  • 方法的多态:方法的重载 和 重写就体现多态
  • 对象的多态:【核心】
    • 一个对象的编译类型和运行类型可以不一致。例如:Animal animal = new Dog();可以让一个父类的引用指向子类的对象。animal的编译类型是Animal,运行类型是Dog
    • 编译类型在定义对象时,就确定了,不能改变
    • 运行类型是可以变化的。
    • 编译类型看定义时 = 左边,运行类型看 = 右边。

注意事项

  • 两个类存在继承关系
  • 多态是向上转型
    • 本质:父类的引用指向子类对象
    • 语法:父类类型 引用名 = new 子类类型()
    • 特点:编译类型看左边,运行类型看右边
  • 多态的向下转型
    • 语法:子类类型 引用名 = (子类类型) 父类引用
    • 只能强转父类的引用,不能强转父类的对象
    • 要求父类的引用必须指向的是当前目标类型的对象
    • 可以调用子类类型中所有的成员日
1
2
3
4
Animal animal = new Dog(); // 向上转型
Dog dog = (Dog) animal; // 向下转型

Cat cat = (Cat) animal; // 错误,animal原来是Dog类型,不能转为cat

属性重写:

  • 属性没有重写之说,属性的值看编译类型。
  • instanceof 比较操作符,用于判断对象的运行类型是否为XX类型或XX类型的子类型。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Test{
public static void main(String[] args){
Base base = new Sub();
System.out.println(base.count); // 10,看编译类型

System.out.println(base instanceof Sub); // True
System.out.println(base instanceof Base); // True

Sub sub = new Sub();
System.out.println(sub instanceof Sub); // True
System.out.println(sub instanceof Base); // True
}
}

class Base{
int count = 10;
}

class Sub extends Base{
int count = 20;
}

java动态绑定机制 ⭐

  • Java重要特性:动态绑定机制
  • 当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定
  • 当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package com.xzt.poly._dynamic;

public class DynamicBinding {
public static void main(String[] args) {
A a = new B();
System.out.println(a.sum()); // 30 动态绑定机制,所以调用父类的sum,所以 getI()是B的为20 20 + 10
System.out.println(a.sum1()); // 20 属性没有动态绑定机制,所以调用父类的sum1,所以 i为10 10 + 10
}
}

class A{
public int i = 10;

public int sum(){
return getI() + 10;
}

public int sum1(){
return i + 10;
}

public int getI() {
return i;
}
}

class B extends A{
public int i = 20;

// public int sum(){
// return getI() + 20;
// }

// public int sum1(){
// return i + 20;
// }

@Override
public int getI() {
return i;
}
}

多态的应用

  • 多态数组:数组的定义类型为父类型,里面保存的实际元素类型为子类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public class Tes{
public static void main(String[] args){
Person[] person = new Person[2];
Student student = new Student();
Person[0] = student;
Teacher teacher = new Teacher();
Person[1] = teacher;
// 遍历数组
for(int i = 0; i < person.length; i++){
//person[i] 编译类型是Person,运行类型是根据实际情况由JVN来判断
System.out.println(person[i].say());

// 因为teach() 和study() 不存在于Person类中,所以需要判断后进行向下强转
if(person[i] instanceof Student){
Student s = (Student) person[i];
s.study();
}
else if(person[i] instanceof Teacher){
((Teacher) person[i]).teach();
}

}
}
}

class Person{
public String name;
public String age;

public void say(){

}
}
// 学生类
class Student extends Person{
@Override
public void say(){

}
public void study(){

}
}
// 老师类
class Teacher extends Person{
@Override
public void say(){

}
public void teach(){

}
}
  • 多态参数:方法定义的参数类型为父类,实参类型允许是子类类型。

Object 类详解

equal方法

  • == 既可以判断基本类型,又可以判断引用类型
  • == 如果判断基本类型,判断的是值是否相等,
  • == 如果判断引用类型,判断的是地址是否相等,即判断的是否是同一个对象
  • equalObject类中的方法,只能判断引用类型
  • 默认判断的是地址是否相等,子类中往往重写该方法,用于判断内容是否相等。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// String类中重写的equals方法
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}

hashCode方法

  • 提高具有哈希表结构的容器的效率
  • 两个引用,如果指向的是同一个对象,则哈希值肯定是一样
  • 两个引用,如果指向的是不同对象,则哈希值不一样
  • 哈希值主要是根据地址号来的,不能完全将哈希值等价于地址
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Test{
public static void main(String[] args){
AA aa = new AA();
AA aa1 = new AA();
AA aa2 = aa;
System.out.println(aa.hashCode);
System.out.println(aa1.hashCode); //aa1 和 aa的hashcode相同
System.out.println(aa2.hashCode); // aa2 和 aa的hashcode相同
}
}

class AA{

}

toString方法

默认返回:全类名 + @ + 哈希值的十六进制

1
2
3
4
5
// Object 类中的toString方法
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
// Integer.toHexString(hashCode()); 将对象以hashcode值转成十六进制返回
  • 重写toString方法,在类中使用快捷键alt + insert 然后选中toString即可
  • 当直接输出一个对象时,toString方法会被默认调用

finalize方法

  • 当对象被回收时,系统会自动调用该对象的finalize方法。子类可以重写该方法,做一些释放资源的操作
  • 什么时候被回收:当某个对象没有任何引用时,则jvm就认为该对象是一个垃圾对象,就会使用垃圾回收机制来销毁该对象,在销毁该对象前,会先调用finalize方法
  • 垃圾回收机制的调用,是由系统来决定(即有自己的GC算法),也可以通过System.gc()主动触发垃圾回收机制。

断点调试

在断点调试过程中,试运行状态,是以对象的运行类型来执行的。

  • 断点调试是指在程序的某一行设置一个断点,调试时,程序运行到这一行就会停住,然后你可以一步一步往下调试,调试过程中可以看各个变量当前的值,出错的话,调试到出错的代码行即显示错误,停下。进行分析从而会找到这个Bug

快捷键

快捷键 功能
F7 跳入方法内
F8 跳过,逐行执行代码
shift + F8 跳出方法
F9 resume,执行到下一个断点
正在加载今日诗词....