JAVA基础

Java

cmd命令

盘的名称+冒号:进入该盘

dir:显示该路径下所有文件

cd+路径:进入某一目录

cd…:退到上一级

打开程序:进入exe文件的目录,直接输入exe可以启动

用cmd编译并运行Java

C:\Users\张瑞枝>D:
//进入盘
D:\>cd eclipse-workspace\Homework\src
//进入Java文件所在目录
D:\eclipse-workspace\Homework\src>javac Helloworld.java
//用Javac编译Java文件
D:\eclipse-workspace\Homework\src>java Helloworld
Helloworld!
//用Java运行文件

‘\t’ 制表符 将前面字符串的长度变成8或者8的倍数,便于对齐

标识符:起名字,可由数字、字母、下划线(_)和美元符($)组成

Java结构:project - module - package - class

二进制:0b开头

十进制:不加开头

八进制:0开头

十六进制:0x开头

byte:1个字节

short:2个字节

int:4个字节

long:8个字节

long n = 9999999999L;
float f = 10.1F;
//上面两个赋值时需要在后面加相应字母,最好大写
boolean b = true;
//Java中布尔类型只有false和true,没有0和1
//键盘录入
//快速生成第三行:psvm
//快速生成打印:sout
import java.util.Scanner;//导包
public class ScannerDemo {
	public static void main(String[] args) {
		Scanner sc=new Scanner(System.in);//创建对象
		int i=sc.nextInt();//接收数据
		int k=sc.nextInt();
		System.out.println(i);
		System.out.println(k);
	}
}

数据类型

基本数据类型:整数类型、浮点数类型、布尔类型、字符类型

直接存储数据,存在栈内存中

赋值给其他变量,也是赋的真实的值

引用数据类型:除了左边的其他所有类型

存储的是数据在其他空间的地址值

赋值给其他变量,赋的是地址值

JAVA基础_第1张图片

参数传递

如果直接传基本数据类型,则两个方法之间不会相互影响

JAVA基础_第2张图片

如果传的是引用数据类型,传递的是地址,则会改变地址所在处的值

JAVA基础_第3张图片

运算符

运算符的类型转换

隐式转换

取值范围小的数值 --> 取值范围大的数值

byte --> short --> int --> long --> float --> double

byte、short、char三种类型的数据在运算的时候,都会直接先提升为int,然后再进行运算

强制转换
public class Main {
    public static void main(String[] args) {
        int i = 8;
        double k =(double)(i * i);
        System.out.println(k);
    }
}

64.0

运算符

++和–在同一行时放在前面和后面都一样

否则b=a++是先赋值后自增,b=++a是先自增后赋值

+= 、-= 、*= 、/= 、%=都隐藏着强制类型转换

且:& 两边都要判断

或:|

左移:<< 向左移动,低位补0

右移:>> 向右移动,高位补0或1(正数补0,负数补1)

无符号右移:>>>(向右移动,高位补0)

异或:^ 相同为false,不同为true

上面是对二进制补码进行操作

非:!

短路逻辑运算符

而且:&& 前面的判断为假就不判断下一个

或:|| 前面的判断为假就不判断下一个

打印

//把ln删掉时没有换行
public class practice1 {
    public static void main(String[] args) {
        int[] a = {1,2,3,4,5};
        int b = a[0];
        a[0] = a[4];
        a[4] = b;
        for (int i = 0; i < a.length; i++) {
            System.out.print(a[i] + ",");
        }
        //下面这个用法和c++的printf相似
        System.out.printf("你好%d",5);
    }
}
5,2,3,4,1,你好5

判断与循环及键盘录入

//“:”可以换成“->”,不过要版本高,就可以省略break
public class Main {
    public static void main(String[] args) {
       int number = 1;
       switch(number){
           case 1 :{
               System.out.println("one");
               break;
           }
           case 2 :{
               System.out.println("two");
               break;
           }
           case 3 :{
               System.out.println("three");
               break;
           }
           default :{
               System.out.println("none");
               break;
           }
       }
    }
}
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        //录入整数
        //录入字符串为next()或nextLine()
        int day = sc.nextInt();
        switch(day){
            case 1: case 2: case 3: case 4:case 5:
               System.out.println("工作日");
               break;
            case 6:case 7:
                System.out.println("休息日");
                break;
           default :{
               System.out.println("none");
           }
        }
    }
}
//键盘录入
//第一套体系:nextInt(),nextDouble(),next()(接受字符串,遇到空格、制表符、回车就不接收了)
//第二套体系:nextLine(),接受字符串,遇到空格和制表符仍然可以接受,遇到回车不接收
//如果用了体系一的再用体系二,可能会导致nextLine()接收不到

数组

每个方法依次放入栈内存中,main方法放在最下面

每new一个就在堆里开辟一个空间,存储数组中的数据,故数组本身是地址值

//静态数组初始化 
//两种写法
public class array {
    public static void main(String[] args) {
        int[] array1 = new int[]{1,2,3};
        int[] array2 = {1,2,3};
        String[] array3 = new String[]{"aa","bb","cc"};
        String[] array4 = {"aa","bb","cc"};
        double[] array5 = new double[]{1.1,2.2,3.3};
        double[] array6 = {1.1,2.2,3.3};
        System.out.println(array1);
        System.out.println(array5);
    }
}
[I@4554617c
[D@1540e19d
//[:表示当前是一个数组
//D:表示当前数组的元素都是double类型的
//I:表示当前数组的元素都是int类型的
//@:表示一个间隔符号(固定格式)
//4554617c和1540e19d才是真正的地址值(十六进制)
//数组遍历
//快速生成for那一行:arr.fori
public class array {
    public static void main(String[] args) {
        int[] arr = {1,2,3,4,5};
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }
    }
}
//动态初始化数组
public class array {
    public static void main(String[] args) {
        String[] s = new String[50];
        s[0] = "haha";
        s[1] = "yes";
        System.out.println(s[0]);
        System.out.println(s[1]);
        System.out.println(s[2]);

        int[] a = new int[5];
        System.out.println(a[0]);
        System.out.println(a[1]);
    }
}
haha
yes
null
0
0
//数组默认初始化规律
//整数类型:0
//小数类型:0.0
//字符类型:空格字符
//布尔类型:false
//引用数据类型:null
//二维数组的初始化
public class DoubleArray {
    public static void main(String[] args) {
        int[][] arr1 = new int[][]{{1,2},{3,4}};
        int[][] arr2 = {
                {1,2},
                {3,4}
        };
        int[][] arr3 = new int[3][4];
        System.out.println(arr1[0]);
        System.out.println(arr1[0][0]);
    }
}
[I@4554617c
1
//二维数组中每一项是一维数组
public class DoubleArray {
    public static void main(String[] args) {
        int[][] arr3 = new int[2][];
        int[] arr1 = {1,2,3,4,5};
        int[] arr2 = {6,7,8};
        //赋值的改变是直接改变地址
        arr3[0] = arr1;
        arr3[1] = arr2;
        for (int i = 0; i < arr3.length; i++) {
            for (int j = 0; j < arr3[i].length; j++) {
                System.out.print(arr3[i][j]);
            }
        }
    }
}
12345678

随机数

public class array {
    public static void main(String[] args) {
        int[] a = new int[10];
        Random r = new Random();
        for (int i = 0; i < a.length; i++) {
            //r.nextInt(100)生成0到99的随机数,加一则得到1到100随机数
            int num = r.nextInt(100) + 1;
            a[i] = num;
        }
        for (int i = 0; i < a.length; i++) {
            System.out.println(a[i]);
        }

    }
}

方法的重载

  • 在同一个类中,定义了多个同名的方法,这些同名的方法具有同种的功能

  • 每个方法具有不同的参数类型或参数个数,这些同名的方法,就构成了重载关系

  • 简单记:同一个类中,方法名相同,参数不同的方法,与返回值无关。

    参数不同:个数不同、类型不同、顺序不同

面向对象

一些规范:类名首字母大写每个单词第一个字母大写,方法名及其他名首字母小写,第二个单词开始每个单词第一个字母大写。

  • 用来描述一类事物的类,叫做Javabean类

    在Javabean类中,是不写main方法的

  • 编写main方法的类,叫做测试类

    我们可以在测试类中创建Javabean类的对象并进行赋值调用

//创建类
public class Phone {
    //成员变量
    String brand;
    double price;

    //成员方法
    public void call(){
        System.out.println("手机在打电话");
    }
    public void playGame(){
        System.out.println("手机在玩游戏");
    }
}

//创建对象并赋值调用
public class PhoneTest {
    public static void main(String[] args) {
        Phone p = new Phone();
        p.brand = "华为";
        p.price = 3999.9;
        System.out.println(p.brand);
        System.out.println(p.price);
        p.call();
        p.playGame();
    }
}
华为
3999.9
手机在打电话
手机在玩游戏

封装

对象有什么属性,就封装其属性对应的行为。

private

  • 是一个权限修饰符
  • 可以修饰成员(成员变量和成员方法)
  • 被private修饰的成员只能在本类中才能访问
  • 目的是防止值被随意错误更改
//创建类
//提供setXxx方法,用于给成员变量赋值,方法用public修饰
//提供getXxx方法,用于获取成员变量的值,方法用public修饰
public class BoyFriend {
    private String name;
    private int age;

    public void setName(String n){
        name = n;
    }
    public String getName(){
        return name;
    }

    public void setAge(int a){
        if(a>=18 && a<=30) age = a;
        else System.out.println("非法参数");
    }
    public int getAge(){
        return age;
    }

    public void sing(){
        System.out.println("男朋友在唱歌");
    }
    public void dance(){
        System.out.println("男朋友在跳舞");
    }
}

//创建对象并赋值调用
//这里打bf.name或bf.age会出错
public class BoyFriendTest {
    public static void main(String[] args) {
        BoyFriend bf = new BoyFriend();
        bf.setAge(40);
        bf.setName("薛之谦");
        System.out.println(bf.getName());
        System.out.println(bf.getAge());
        bf.sing();
        bf.dance();
    }
}

this

//创建类
public class Phone {
    private String brand;
    private double price;

    public void method(){
        double price = 10.0;
        //就近原则,所以下面的price是局部变量的price
        System.out.println(price);
        //this则表示成员位置的值
        System.out.println(this.price);
    }
}

//创建对象,赋值并调用方法
public class PhoneTest {
    public static void main(String[] args) {
        Phone p = new Phone();
        p.method();
    }
}
10.0
0.0
//创建类,使用this
public class BoyFriend {
    private String name;
    private int age;

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

    public void setAge(int age){
        if(age>=18 && age<=30) this.age = age;
        else System.out.println("非法参数");
    }
    public int getAge(){
        return age;
    }

    public void sing(){
        System.out.println("男朋友在唱歌");
    }
    public void dance(){
        System.out.println("男朋友在跳舞");
    }
}

//创建对象,赋值并调用方法
public class BoyFriendTest {
    public static void main(String[] args) {
        BoyFriend bf = new BoyFriend();
        bf.setAge(40);
        bf.setName("薛之谦");
        System.out.println(bf.getName());
        System.out.println(bf.getAge());
        bf.sing();
        bf.dance();
    }
}
非法参数
薛之谦
0
男朋友在唱歌
男朋友在跳舞

构造方法

  1. 方法名与类名相同,大小写也相同
  2. 没有返回值类型,void也没有
  3. 没有具体的返回值,不能用return返回结果数据
  4. 每创建一次对象,虚拟机就会调用一次构造方法,不能手动调用构造方法

如果定义了构造方法,则系统不再提供默认构造方法。

无论是否使用,都手动书写无参数构造方法,和带全部参数的构造方法。

//创建类
public class Student {
    private String name;
    private int age;

    //默认的构造方法
    //这里也体现了构造方法的重载
    //快捷键:alt+insert,选择Constructor
    //另外可以用插件,右键选择Ptg To JavaBean快速构造JavaBean
    public Student(){
    }
    
    //自己定义的构造方法,最好两种构造方法都要写
    public Student(String name,int age){
        this.name = name;
        this.age = age;
    }

    //快捷键:alt+insert,选择Getter and Setter
    public void setName(String name){
        this.name = name;
    }
    public String getName(){
        return name;
    }
    public void setAge(int age){
        this.age = age;
    }
    public int getAge(){
        return age;
    }
}

//创建对象,赋值并调用方法
public class StudentTest {
    public static void main(String[] args) {
        Student s = new Student();
        //可以在创建对象时直接赋值
        Student e = new Student("薛之谦",40);
        System.out.println(s.getName());
        System.out.println(s.getAge());
        System.out.println(e.getName());
        System.out.println(e.getAge());
    }
}
null
0
薛之谦
40

JavaBean

标准的JavaBean类

  1. 类名见名知意
  2. 所有成员变量使用private修饰
  3. 提供至少两个构造方法:无参构造方法和带全部参数的构造方法
  4. 提供每个成员变量的setXxx和getXxx

内存

栈内存:存储方法,先进后出

堆内存:存储new的对象

方法区:存储类

对象存储的是在堆内存的地址,成员方法存储的是在方法区的地址

JAVA基础_第4张图片

下面这个新建两个对象时,class文件不需要再次加载在方法区了,可直接使用

下面是两个引用指向同一个对象,则是地址值的赋值

JAVA基础_第5张图片

this的内存原理

this的本质:代表方法调用者的地址

s存储的是在堆内存中的地址,调用method方法后,this表示的就是s存储的地址

JAVA基础_第6张图片

setName中this的原理也是如此

JAVA基础_第7张图片

字符串

构造方法

字符串是java定义好的一个类,有一些构造方法

public class s1 {
    public static void main(String[] args) {
        String s1 = "abc";
        System.out.println(s1);

        //空参构造
        String s2 = new String();
        System.out.println(s2);

        //传递字符串构造
        String s3 = new String("abc");
        System.out.println(s3);

        //传递字符数组构造
        char[] c = {'a','b','c','d'};
        String s4 = new String(c);
        System.out.println(s4);

        //传递字节数组,根据字节数组对应在ASCII表中字符创建一个字符串
        byte[] b ={97,98,99,100};
        String s5 = new String(b);
        System.out.println(s5);
    }
}

abc

abc
abcd
abcd

字符串的比较

//基本数据类型比较的是数据值,引用数据类型比较的是地址值
public class s1 {
    public static void main(String[] args) {
        //两个字符串都是存储在串池中,结果为相等
        String s1 = "abc";
        String s2 = "abc";
        System.out.println(s1 == s2);

        //每new一次会在堆中开辟一个新的地址,故两个的地址不同,结果为不相等
        String s3 = new String("abc");
        String s4 = new String("abc");
        System.out.println(s1 == s3);

        System.out.println(s1 == s3);
        //键盘录入的也会new一个String,故地址不同
        Scanner sc = new Scanner(System.in);
        String s5 = sc.next();
        System.out.println(s1 == s5);
    }
}
true
false
false
abc
false
public class s1 {
    public static void main(String[] args) {
        String s1 = "abc";
        String s2 = "abc";
        String s3 = "ABc";
        //equals()和equalsIgnoreCase()可以用来比较字符串的内容,返回的是布尔类型
        //equals()方法完全相同才是true
        //erualsIgnoreCase()方法不比较大小写
        boolean result1 = s1.equals(s2);
        boolean result2 = s1.equals(s3);
        boolean result3 = s1.equalsIgnoreCase(s3);
        System.out.println(result1);
        System.out.println(result2);
        System.out.println(result3);
    }
}
true
false
true

字符串索引

public class ForString {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String s = sc.next();
        //快捷键:s.length().fori
        //s.length().forr倒着遍历
        for(int i = 0;i< s.length();i++){
            //charAt()方法,用来索引
            System.out.print(s.charAt(i));
        }
    }
}
hahaxzqloveyou
hahaxzqloveyou
    
public class ForString {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String s = sc.next();
        int upper = 0, lower = 0, number = 0;
        for (int i = 0; i < s.length(); i++) {
            if(s.charAt(i) >= 'a' && s.charAt(i) <= 'z') lower ++;
            else if(s.charAt(i) >= 'A' && s.charAt(i) <= 'Z') upper ++;
            else if(s.charAt(i) >= '0' && s.charAt(i) <= '9') number ++;
        }
        System.out.println(upper + ", " + lower + ", " + number);
    }
}
loveXZQ1314
3, 4, 4

substring

public class Substring {
    public static void main(String[] args) {
        String s1 = "anbcgak";
        //包前不包后,都是索引
        String s2 = s1.substring(2,5);
        //下面这个表示索引3一直截取到字符串最后
        String s3 = s1.substring(3);
        System.out.println(s1);
        System.out.println(s2);
        System.out.println(s3);
    }
}
anbcgak
bcg
cgak

replace

public class StringDemo1 {
    public static void main(String[] args) {
        String num = "lovexzqmd";
        String s = new String();
        //参数:被替代的字符串,替代的字符串
        s = num.replace("md","1314");
        System.out.println(s);
    }
}
lovexzq1314

StringBuilder

一个类,StringBuilder可以看成是一个容器,创建之后里面的内容是可变的。

作用是提高字符串的操作效率。

//两个构造方法
//空参构造
public StringBuilder()
//根据字符串的内容,创建可变字符的对象
public StringBuilder(String s)

常用方法

public class StringDemo2 {
    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder();
        //打印出来的不是地址值而是属性值
        //因为这是Java已经写好的类,Java在底层对它做了特殊处理
        System.out.println(sb);

        //可以直接对类中对象修改
        //append()向字符串添加内容
        sb.append(1);
        sb.append(2.3);
        sb.append(true);
        System.out.println(sb);
        //可以使用链式编程,如下
        //sb.append(1).append(2.3).append(true)

        //reverse反转字符串
        sb.reverse();
        System.out.println(sb);
        
        //toString方法把它变成字符串类型
        String s = sb.toString();
        System.out.println(s);
    }
}


12.3true
eurt3.21
eurt3.21  

StringJoiner

和StringBuilder一样,可以看成是一个容器,创建后里面的内容是可变的。

作用是提高字符串的操作效率。

//构造方法 没有空参构造
public StringJoiner(间隔符号)
public StringJoiner(间隔符号,开始符号,结束符号)
public class StringDemo5 {
    public static void main(String[] args) {
        StringJoiner sj = new StringJoiner("---");
        //add方法
        sj.add("aaa").add("bbb").add("ccc");
        System.out.println(sj.toString());

        StringJoiner s = new StringJoiner(",","[","]");
        s.add("aaa").add("bbb").add("ccc");
        System.out.println(s);
        System.out.println(s.length());
    }
}
aaa---bbb---ccc
[aaa,bbb,ccc]
13

字符串拼接的底层原理

情况一:拼接的时候没有变量参与

会复用串池中的字符串,直接赋值也会复用串池中的字符串

JAVA基础_第8张图片

情况二:拼接的时候有变量参与

会创建StringBuilder对象

JDK7及以前的创建方法

JAVA基础_第9张图片

JAVA基础_第10张图片

JDK8的创建方法

先预估字符串长度,然后创建数组,将对象放进去,再转化为字符串
JAVA基础_第11张图片

字符串拼接时最好用StringBuilder或者StringJoiner

StringBuilder源码分析

  • 默认创建一个容量为16的的字节数组
  • 添加的内容长度小于等于16,直接存
  • 添加的内容长度大于16会扩容(原来的容量*2+2)
  • 如果添加的内容长度大于扩容之后的长度,则直接创建添加的内容长度的数组
//注意这里是初始添加的情况
public class StringDemo6 {
    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder();
        //capacity()返回容量
        System.out.println(sb.capacity());
        System.out.println(sb.length());

        sb.append("qwertyuiopasdfghjklzxcvbnm");
        System.out.println(sb.capacity());
        System.out.println(sb.length());
        
        StringBuilder sb2 = new StringBuilder();
        sb2.append("qwertyuiopasdfghjklzxcvbnm1234567890");
        System.out.println(sb2.capacity());
        System.out.println(sb2.length());
    }
}
16
0
34
26
36
26

toCharArray

public class StringDemo7 {
    public static void main(String[] args) {
        String s = "abcde";
        char[] c = s.toCharArray();
        //得到的数组是['a','b','c','d','e']
    }
}

包装类

基本数据类型 包装类
byte Byte
short Short
char Character
int Integer
long Long
float Float
double Double
boolean Boolean

static

静态方法中没有this关键字

静态方法中,只能调用静态

非静态方法可以访问所有

被static修饰的成员变量,叫做静态变量。

  • 被该类所有对象共享
  • 不属于对象,属于类
  • 随着类的加载而加载,优先于对象存在

被static修饰的成员变量,叫做静态方法。

  • 多用在测试类和工具类中
  • Javabean类中很少会用
//创建类
public class Student {
    private String name;
    private int age;
    private String gender;
    //创建一个static的属性,则该类共享这一属性的值
    public static String teacher;

    public Student() {
    }

    public Student(String name, int age, String gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }
}

public class StudentTest {
    public static void main(String[] args) {
        Student s1 = new Student("xzq",40,"男");
        Student s2 = new Student("zrz",19,"女");
        //直接对类进行赋值
        Student.teacher = "阿玮老师";
        System.out.println(s1.getName() + ", " + s1.getGender() + ", " + s1.getAge() + ", " + s1.teacher);//xzq, 男, 40, 阿玮老师
        System.out.println(s2.getName() + ", " + s2.getGender() + ", " + s2.getAge() + ", " + s2.teacher);//zrz, 女, 19, 阿玮老师
    }
}

static内存图

静态区在堆内存中

JAVA基础_第12张图片

工具类

帮我们做一些事情,不描述任何事物的类。

//类名见名知意
public class ArrUtil {
    //私有化构造方法
    private ArrUtil(){}

    //方法定义为静态
    public static int getMax(){}
    public static int getMin(){}
    public static int getSum(){}
    public static int getAvg(){}

}

继承

当类与类之间,存在共性的内容,并满足子类是父类中的一种,就可以考虑使用继承来优化代码。

继承可以把多个子类中重复的代码抽取到父类中,子类可以直接使用,减少代码冗余,提高代码的复用性。

//继承格式
//子类可以使用父类的属性和行为
//子类可以在父类的基础上新增其他功能,子类更强大
public class 子类 extends 父类 {}

Java支持单继承,不支持多继承,支持多层继承。

单继承:一个子类只能继承一个父类

多继承:一个子类可以继承多个父类

多层继承:子类A继承父类B,父类B继承父类C

每个类都直接或间接继承于Object类

画图法:从子类到父类画,抽取共性

写代码:从父类到子类

子类只能访问父类中非私有的成员。

//构造方法私有还是非私有都不能被继承
//成员变量私有还是非私有都可以被继承,私有的可以被继承,不能直接使用
//成员方法可以被添加到虚方法表的可以被继承,否则不可以被继承

继承的内存图如下

JAVA基础_第13张图片

A继承C时不是一层一层往上找的

只有父类中的虚方法才能被子类继承

虚方法如下图

JAVA基础_第14张图片

//成员变量的访问:就近原则
//先在本类找,找不到再去父类找
public class Fuzi {
    public static void main(String[] args) {
        Zi z = new Zi();
        z.show();
    }
}

class Fu{
    String name = "Fu";
}

class Zi extends Fu{
    String name = "Zi";
    public void show(){
        String name = "ziShow";
        //就近原则
        System.out.println(name);//ziShow
        //调用本类的
        System.out.println(this.name);//Zi
        //调用父类的
        //最多只能调用一个直接父类的
        System.out.println(super.name);//Fu
    }
}
//继承中方法的调用:就近原则
public class Test {
    public static void main(String[] args) {
        OverseasStudent os = new OverseasStudent();
        os.lunch();
    }
}

class Person{
    public void eat(){
        System.out.println("eating");
    }

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

class OverseasStudent extends Person{

    public void lunch(){
        //先在本类找,没有再去父类找
        this.eat();
        this.drink();

        //直接去父类找
        super.eat();
        super.drink();
    }

    public void eat(){
        System.out.println("eating other");
    }

    public void drink(){
        System.out.println("drinking other");
    }
}

class Students extends Person{
    public void lunch(){
        //先在本类找,再去父类找
        eat();
        drink();
    }
}


eating
drinking
eating other
drinking other
eating
drinking

方法的重写

  • 当父类的方法不能满足子类的需求时,需要进行方法重写
  • @Override是放在重写后的方法上,校验子类重写时语法是否正确。
class Person{
    public void eat(){
        System.out.println("eating");
    }

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

class OverseasStudent extends Person{

    public void lunch(){
        this.eat();
        this.drink();

        super.eat();
        super.drink();
    }

    @Override
    public void eat(){
        System.out.println("eating other");
    }

    @Override
    public void drink(){
        System.out.println("drinking other");
    }
}

方法重写的本质:覆盖父类给的虚方法表

JAVA基础_第15张图片

JAVA基础_第16张图片

public class DogTest {
    public static void main(String[] args) {
        Husky h = new Husky();
        h.eat();
        h.drink();
        h.lookHome();
        h.destory();

        ChineseDog cd = new ChineseDog();
        cd.eat();
        cd.drink();
        cd.lookHome();
    }
}

public class Dog {

    public void eat(){
        System.out.println("吃狗粮");
    }
    public void drink(){
        System.out.println("喝水");
    }

    public void lookHome(){
        System.out.println("看家");
    }

}

public class Husky extends Dog{
    public void destory(){
        System.out.println("拆家");
    }
}

public class ShapiDog extends Dog{
    //这里可以直接快捷写eat()
    @Override
    public void eat() {
        //先调用父类的,再进行补充
        super.eat();
        System.out.println("吃骨头");
    }
}

public class ChineseDog extends Dog{

    //同样可以快捷键eat()
    @Override
    public void eat(){
        System.out.println("吃剩饭");
    }
}

继承中构造方法的访问

//子类中所有构造方法默认先访问父类中的无参构造,再执行自己
//因为子类初始化前,一定要调用父类构造方法完成父类数据空间的初始化
//子类构造方法第一句默认是super(),不写也存在,必须写在第一句
//如果要调用父类有参构造,必须手动写super进行调用
public class Person {
    String name;
    int age;

    public Person() {
        System.out.println("这里是父类");
    }

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

public class Student extends Person{
    public Student(){
        //先调用父类的无参构造,再调用子类的
        super();
        System.out.println("这里是子类");
    }

    //直接传递参数,传到super里,就会去父类找到对应的有参构造
    public Student(String name,int age){
        super(name,age);
    }
}

public class PersonTest {
    public static void main(String[] args) {
        Student s1 = new Student();
        /*这里是父类
          这里是子类*/
        Student s2 = new Student("xzq",40);
        System.out.println(s2.name + ", " + s2.age);//xzq, 40
    }
}
public class Person {
    String name;
    int age;

    public Person() {
        System.out.println("这里是父类");
    }

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

public class Student extends Person{
    public Student(){
        //表示调用本类其他构造方法,即下面的构造方法
        //没传参时默认传的是调用者的this
        this(null,18);
    }

    public Student(String name,int age){
        super(name,age);
    }
}

public class PersonTest {
    public static void main(String[] args) {
        Student s1 = new Student();
        System.out.println(s1.name + ", " + s1.age);
    }
}

多态

多态即同类型的对象,表现出的不同形态。

//多态优势:方法中,使用父类型作为参数,可以接收所有子类对象
//多态弊端:不能使用子类特有的功能
//强制类型转换可以转换成真正的子类类型,从而调用子类独有功能

表现形式:父类类型 对象名称 = 子类对象;

多态的前提

  • 有继承关系
  • 有父类引用指向子类对象
  • 有方法重写
public class Person {
    private String name;
    private int age;

    public Person() {
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void show(){
        System.out.println(name + ", " + age);
    }
}

public class Student extends Person{
    @Override
    public void show() {
        System.out.println(this.getName() + ", " + this.getAge());
    }
}

public class Teacher extends Person{
    @Override
    public void show() {
        System.out.println(this.getName() + ", " + this.getAge());
    }
}

public class Test {

    public static void main(String[] args) {

        Student s = new Student();
        s.setName("xzq");
        s.setAge(40);

        Teacher t = new Teacher();
        t.setName("zrz");
        t.setAge(19);

        show(s);
        show(t);
    }

    //使用父类型作为参数,可以接收所有子类对象
    //不过必须要有方法的重写
    public static void show(Person p){
        p.show();
    }
}
public class AnimalTest {
    public static void main(String[] args) {
        Animal a = new Dog();
        //访问成员属性时:编译看左边,运行看左边
        //javac编译时,会看父类中有没有这个成员变量,有则编译成功,没有则编译失败
        //java运行时,实际获取的是左边父类中成员变量的值
        System.out.println(a.name);//动物
        
        //访问成员方法时:编译看左边,运行看右边
        //javac编译时,会看父类中有没有这个成员方法,有则编译成功,没有则编译失败
        //java运行时,实际获取的是右边子类中的成员方法
        a.show();//dog show
    }
}

class Animal{
    String name = "动物";

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

class Dog extends Animal{
    String name = "狗";

    @Override
    public void show() {
        System.out.println("dog show");
    }
}

JAVA基础_第17张图片

public class AnimalTest {
    public static void main(String[] args) {
        Animal a = new Dog();
       
        //instanceof用于判断前面那个对象是不是属于后面那个类
        if(a instanceof Dog){
            //若不强转,则用a无法调用子类方法中的eat()
            Dog d = (Dog) a;
            d.eat();//dog is eating
        }else if(a instanceof Cat){
            Cat c = (Cat) a;
            c.eat();
        }
        
        //另一个简单写法,要新版本可用
        //如果a是Dog类则强转且转后命名为d
        if(a instanceof Dog d){
            d.eat();
        }else if(a instanceof Cat c){
            c.eat();
        }
    }
}

class Animal{
    String name = "动物";

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

class Dog extends Animal{
    String name = "狗";

    @Override
    public void show() {
        System.out.println("dog show");
    }

    public void eat(){
        System.out.println("dog is eating");
    }
}

class Cat extends Animal{
    @Override
    public void show() {
        System.out.println("cat show");
    }

    public void eat(){
        System.out.println("cat is eating");
    }
}

包就是文件夹,用来管理各种不同功能的Java类。

**包名的规则:公司域名反写 + 包的作用,需要全部英文小写,见名知意。**例:com.itheima.domain

一个类的全类名应为 包名.类名 例:com.itheima.domain.Student

  • 使用同一个包中的类时,不需要导包
  • 使用java.lang包中的类时,不需要导包
  • 其他情况都要导包,例import com.itheima.domain.Student则默认用到的Student类都是导包中的这个类
  • 如果同时使用两个包中的同名类,需要用全类名

final

  • final修饰方法,表示该方法是最终方法,不能被子类重写
  • final修饰类,表示该类是最终类,不能被继承
  • final修饰变量,叫做常量,只能被赋值一次

常量命名规范

  • 单个单词:全部大写
  • 多个单词:全都大写,单词之间用下划线隔开
//final修改基本数据类型,记录的值不能发生改变
//final修饰引用数据类型,记录的地址值不能发生改变,内部的属性值还是可以改变
public class Test {

    public static void main(String[] args) {

        final Student S = new Student("xzq",40);
        S.setName("zrz");
        S.setAge(19);
        System.out.println(S.getName() + ", " + S.getAge());//zrz, 19
        
        final int[] ARR = {1,2,3,4,5};
        ARR[0] = 10;
        ARR[1] = 20;
        for (int i = 0; i < ARR.length; i++) {
            System.out.println(ARR[i]);
        }/*10
           20
           3
           4
           5*/
    }
}

class Student{
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

权限修饰符

用来控制一个成员能被访问的范围的。可以修饰成员变量、方法、构造方法、内部类。

JAVA基础_第18张图片

代码块

构造代码块

public class Test {

    public static void main(String[] args) {

        Student s1 = new Student();
        Student s2 = new Student("xzq",40);
    }
}

/*我要开始构造了
  有参构造
  我要开始构造了
  无参构造*/

class Student{
    private String name;
    private int age;
   
    //构造代码块
    //写在成员位置的代码块
    //可以把多个构造方法中重复的代码抽取出来
    //每新建一个对象就会执行一次
    //先执行构造代码块,再执行构造方法
    {
        System.out.println("我要开始构造了");
    }

    public Student() {
        System.out.println("有参构造");
    }

    public Student(String name, int age) {
        System.out.println("无参构造");
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
public class Test {

    public static void main(String[] args) {

        Student s1 = new Student();
        Student s2 = new Student("xzq",40);
    }
}

/*
我要开始构造了
有参构造
无参构造
*/

class Student{
    private String name;
    private int age;

    //静态代码块
    //随着类的加载而加载,只会被执行一次
    //可以用来进行数据初始化
    static {
        System.out.println("我要开始构造了");
    }

    public Student() {
        System.out.println("有参构造");
    }

    public Student(String name, int age) {
        System.out.println("无参构造");
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

抽象类和抽象方法

//抽象类不能实例化,不能创建对象
//抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
//可以有构造方法
//抽象类的子类 要么重写抽象类中的所有抽象方法 要么是抽象类
public class Test {

    public static void main(String[] args) {

        Student s = new Student("xzq",40);
        System.out.println(s.getName() + ", " + s.getAge());//xzq, 40
    }
}

abstract class Person{
    private String name;
    private int age;

    //抽象类可以有构造方法
    //可以在子类创建对象时,给属性进行赋值
    public Person() {
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
    
    //抽象类的格式,没有大括号
    abstract void work();
}

class Student extends Person{

    public Student() {}

    public Student(String name, int age) {
        super(name,age);
    }
    
    //抽象类的子类一定要重写抽象方法
    @Override
    public void work(){
        System.out.println("working");
    }
}

//抽象类的子类也可以是抽象类,但这样就也不能创建对象,只能再来一个子类创建对象
abstract class student2 extends Person{
    
}

接口

接口是一种规则,是对行为的抽象。

  • 接口用关键字interface来定义:public interface 接口名 {}
  • 接口不能实例化
  • 接口和类之间是实现关系,通过implements关键字表示:public class 类名 implements 接口名 {}
  • 接口的子类(实现类)要么重写接口中的所有抽象方法,要么是抽象类
  • 接口和类的实现关系,可以单实现,也可以多实现:public class 类名 implements 接口名1 接口名2 {}
  • 实现类还可以在继承一个类的同时实现多个接口。public class 类名 extends 父类 implements 接口名1 接口名2 {}
//接口中
//成员变量:只能是常量,默认修饰符:public static final
//没有构造方法
//成员方法:只能是抽象方法,默认修饰符:public abstract
public class Test {
    public static void main(String[] args) {

        //可以直接调用,说明了默认static
        System.out.println(Inter.a);//10
    }
}

public interface Inter {

    //默认是public static final int a = 10
    int a = 10;
    //默认是public abstract void method()
    void method();
}

public interface Inter1 {
    public abstract void method1();
    public abstract void method2();
}

public interface Inter2 {
    public abstract void method1();
    public abstract void method2();
}

//可以同时继承多个接口
//当接口中的方法重名时,重写一次即可
public class Inter implements Inter1,Inter2{
    @Override
    public void method1() {

    }

    @Override
    public void method2() {

    }
}
public interface Inter1 {
    public abstract void method1();
}

public interface Inter2 {
    public abstract void method2();
}

//接口之间可以继承,且可以单继承和多继承
public interface Inter3 extends Inter1,Inter2{
    public abstract void method3();
}

//实现了子接口,则要重写子接口和所有父接口的抽象方法,如果父接口有父接口,则也要重写抽象方法
public class Inter implements Inter3{
    @Override
    public void method1() {

    }

    @Override
    public void method2() {

    }

    @Override
    public void method3() {

    }
}
public interface Inter1 {
    public abstract void method1();
    
    //接口中可以有默认方法,default不能省略
    //默认方法可以不被重写,如果被重写要去掉default关键字
    //如果实现了多个接口,都有同名的默认方法,则子类必须要重写
    public default void method2(){
        System.out.println("default method");
    }
}

public class Inter implements Inter1{
    @Override
    public void method1() {
        System.out.println("method1");
    }
    
    //如果要重写是这样的
    @Override
    public void method2() {
        Inter1.super.method2();
    }
}

public class Test {
    public static void main(String[] args) {
        Inter i = new Inter();
        i.method1();//method1
        i.method2();//default method
    }
}
public interface Inter1 {
    
    //接口中静态方法,static关键字不能省略
    public static void show(){
        System.out.println("Inter1 show");
    }
}

public class Inter implements Inter1{
    
    //静态方法不能被重写,因为静态方法无法进入虚方法表
    //下面这个不是重写,只是又写了个一样名字的方法
    public static void show(){
        System.out.println("Inter show");
    }
}

public class Test {
    public static void main(String[] args) {
        
        //静态方法可以直接被类名调用
        Inter1.show();
        Inter.show();
    }
}
//私有方法,只能在本类中被调用
public interface Inter1 {
    public default void show1(){
        System.out.println("Inter1 show1");
        show3();
    }

    public default void show2(){
        System.out.println("Inter1 show2");
        show3();
    }
    
    //普通的私有方法,不用写default,但是是给默认方法服务的
    private void show3(){
        System.out.println("Inter show3");
    }
}
public interface Inter1 {
    public static void show1(){
        System.out.println("Inter1 show1");
        show3();
    }

    public static void show2(){
        System.out.println("Inter1 show2");
        show3();
    }

    //静态的私有方法,一定要写static,给静态方法服务
    private static void show3(){
        System.out.println("Inter show3");
    }
}

接口的应用

//接口类型 j = new 实现类对象();
//当一个方法的参数是接口时,可以传递该接口所以实现类对象,这叫做接口多态。
//适配器设计模式
//如下如果一个接口有五个方法,但是我们只想使用方法3
public interface Inter1 {
    public abstract void method1();
    public abstract void method2();
    public abstract void method3();
    public abstract void method4();
    public abstract void method5();
}

//可以用一个适配器来实现接口,重写全部接口方法并且全部是空实现
//为防止适配器被创建对象,设为abstract
public abstract class Inter1Adapter implements Inter1{

    @Override
    public void method1() {

    }

    @Override
    public void method2() {

    }

    @Override
    public void method3() {

    }

    @Override
    public void method4() {

    }

    @Override
    public void method5() {

    }
}

//再用我们想要的类去继承适配器,重写想要的方法即可
public class Inter extends Inter1Adapter{
    @Override
    public void method3() {
        System.out.println("new method3");
    }
}

内部类

即在一个类的里面,再定义一个类

类的五个成员:属性、方法、构造方法、代码块、内部类

  • 内部类表示的事物是外部类的一部分
  • 内部类单独出现没有任何意义
//内部类可以直接访问外部类的成员,包括私有
//外部类要访问内部类的成员,必须创建对象
public class Car {
    String carName;
    int carAge;
    String carColor;

    public void show(){
        System.out.println(carName);
        //外部类访问内部类成员要先创建对象
        Engine e = new Engine();
        System.out.println(e.engineName);
    }

    class Engine{
        String engineName;
        int engineAge;
        public void show(){
            //内部类可以直接访问外部类成员,因为默认传this
            System.out.println(engineName);
            System.out.println(carName);
        }
    }
}

public class CarTest {
    public static void main(String[] args) {
        Car c = new Car();
        c.carName = "宾利";
        c.carAge = 3;
        c.carColor = "粉色";
        c.show();
    }
}
宾利
null

成员内部类

  • 写在成员位置的,属于外部类的成员。
  • 成员内部类可以被一些修饰符所修饰,比如:private、默认、protected、public、static等
//构建内部类对象的方法
public class Outer {
    String name;
    
    class Inner{
        
    }
    
    //可以在外部类中写一个方法返回内部类
    public Inner getInstance(){
        return new Inner();
    }
}

public class OuterTest {
    public static void main(String[] args) {
        //方法一:按以下格式新建对象
        //要内部类不是私有的情况下才行
        Outer.Inner oi = new Outer().new Inner();

        //方法二:创建外部类对象,再调用方法创建内部类对象
        //通常内部类是私有时使用该方法,可以用Object接收
        Outer ou = new Outer();
        Object o = ou.getInstance();
    }
}
public class Outer {
    private int a = 10;

    class Inner{
        private int a = 20;
        public void show(){
            int a = 30;
            System.out.println(a);//30
            System.out.println(this.a);//20
            //Outer.this 获取了外部类对象的地址值
            System.out.println(Outer.this.a);//10
        }
    }

    public Inner getInstance(){
        return new Inner();
    }
}

public class OuterTest {
    public static void main(String[] args) {
        Outer.Inner oi = new Outer().new Inner();
        oi.show();
    }
}

JAVA基础_第19张图片

内部类的堆内存中有外部类的this,故可以用Outer.this访问外部类的成员变量

静态内部类

  • 静态内部类是成员内部类的一种,即有static修饰时
  • 静态内部类只能访问外部类中的静态变量和静态方法,如果想要访问非静态的需要创建外部类对象
public class Outer {
    int a = 10;
    static int b = 20;

    static class Inner{
        public void show1(){
            //静态内部类可以直接访问静态外部类的成员变量
            System.out.println(b);
            
            //如果要访问外部类的非静态成员变量,需要创建外部类的对象再访问
            Outer o = new Outer();
            System.out.println(o.a);
        }
    }
}
public class Outer {
    static class Inner{
        public void show1(){
            System.out.println("no static");
        }

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

public class OuterTest {
    public static void main(String[] args) {
        //创建静态内部类,直接外部类点就行
        //只要是静态的东西,都可以用类名直接获取
        Outer.Inner oi = new Outer.Inner();
        
        oi.show1();//no static
        //调用静态内部类中的静态方法
        Outer.Inner.show2();//static
    }
}

局部内部类

将内部类定义在方法里面就叫做局部内部类,类似于方法里面的局部变量。

public class Outer {
    int b = 20;

    public void show(){
        int a = 10;

        class Inner{
            
            String name;
            int age;

            public void method1(){
                
                //该类可以直接访问外部类的成员,也可以访问方法内的局部变量
                System.out.println(a);
                System.out.println(b);
                System.out.println("method1");
            }

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

        //外界无法直接使用局部内部类
        //需要在方法内部创建对象并使用
        Inner i = new Inner();
        System.out.println(i.name);
        System.out.println(i.age);
        i.method1();
        Inner.method2();
    }

}

public class OuterTest {
    public static void main(String[] args) {
        Outer o = new Outer();
        o.show();
    }
}

null
0
10
20
method1
static method2

匿名内部类

本质是隐藏了名字的内部类

public interface Swim {
    public void swim();
}

public abstract class Animal {
    public abstract void eat();
}

public class SwimTest {
    public static void main(String[] args) {
        
        //实质上大括号及里面的内容才是匿名的内部类
        //new Swim()则是创建了一个对象
        //这个匿名内部类实现Swim接口,所以要重写swim方法
        //所以这个实质上是匿名内部类的对象
        new Swim(){
            @Override
            public void swim() {
                System.out.println("override swim");
            }
        };//后面有个分号
        
        //这个匿名内部类是重写了Animal的抽象方法
        //所以这个是继承关系,匿名的内部类继承了Animal类
        new Animal(){
            @Override
            public void eat() {
                System.out.println("override eat");
            }
        };
        
        //当要调用一个方法,但是创建对象只是被传参无意义
        //可以用匿名内部类传参
        //匿名内部类的使用场景
        show(
                new Animal() {
                    @Override
                    public void eat() {
                        System.out.println("eating");
                    }
                }
        );
        
        //创建一个接口的实现类对象
        //接口多态
        Swim s = new Swim() {
            @Override
            public void swim() {
                System.out.println("swimming");
            }
        };
        
        new Swim(){
            @Override
            public void swim() {
                System.out.println("swimming");//swimming
            }
        }.swim();//用接口的实现类对象调用自己重写的方法
    }
    
    public static void show(Animal a){
        
        //编译看左边,运行看右边
        a.eat();
    }
}

集合

  • 集合分为单列集合和双列集合
  • 单列集合即一个一个数据添加元素
  • 双列集合是一对一对数据添加元素

Collection

  • Collection即单列集合,包含List和Set两个
  • List序列集合:添加的元素是有序、可重复、有索引
  • Set系列集合:添加的元素是无序、不重复、无索引

Collection是单列集合的祖宗接口,它的功能是全部单列集合都可以继承使用的。

import java.util.ArrayList;
import java.util.Collection;

public class Test {
    public static void main(String[] args) {
        Collection<String> coll = new ArrayList<>();

        //add()方法
        coll.add("aaa");
        coll.add("bbb");
        //返回布尔值
        //如果是往List系列集合添加元素,永远返回true
        //如果是往Set系列集合添加元素,集合中元素不存在返回true,集合中元素存在返回false
        //因为Set系列集合不允许重复
        System.out.println(coll.add("ccc"));//true
        System.out.println(coll);//[aaa, bbb, ccc]

        //clear()方法
        coll.clear();
        System.out.println(coll);//[]

        coll.add("aaa");
        coll.add("bbb");
        //集合元素存在返回true,不存在则返回false
        System.out.println(coll.remove("aaa"));//true
        System.out.println(coll);//[bbb]

        //集合中有这个元素则为true,否则为false
        //contains()方法底层用equals()方法判断,故若集合中存储的是自定义对象,需要重写equals()方法
        System.out.println(coll.contains("bbb"));//true
        
        System.out.println(coll.isEmpty());//false

        System.out.println(coll.size());//1
    }
}


//Collection的contains()方法底层用equals()方法判断,故若集合中存储的是自定义对象,需要重写equals()方法
public class Student {
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    //可以用alt加insert插入重写的equals()方法
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age && Objects.equals(name, student.name);
    }
}

public class StudentTest {
    public static void main(String[] args) {
        Student s1 = new Student("xzq",40);
        Collection<Student> coll = new ArrayList<>();
        coll.add(s1);
        Student s2 = new Student("xzq",40);
        //如果没有重写equals()方法则为false,因为Object类中的equals方法判断的是地址值
        System.out.println(coll.contains(s2));//true
    }
}

迭代器遍历

迭代器在Java中的类是Iterator,迭代器是集合专用的遍历方式。

迭代器在遍历集合的时候是不依赖索引的,是用指针。

//通常需要删除元素时才使用迭代器遍历
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

public class Test {
    public static void main(String[] args) {
        Collection<String> coll = new ArrayList<>();

        coll.add("aaa");
        coll.add("bbb");

        //创建迭代器对象,默认指向0索引处
        Iterator<String> it = coll.iterator();
        
        //hasNext()方法判断当前位置是否有元素
        while(it.hasNext()){
            //next()方法有两个作用:获取当前位置的元素,指针移到下一个位置
            String str = it.next();
            
            //这个if语句会报错
            //迭代器遍历的时候,不能用集合的方法进行增加或者删除
            if("bbb".equals(str)){
                coll.remove("bbb");
            }
            
            //可以用迭代器带的remove方法
            if("bbb".equals(str)){
                it.remove();
            }
            System.out.println(str);//aaa
        }
        //注意点:迭代器遍历完毕,指针不会复位
    }
}

增强for遍历

  • 增强for遍历的底层就是迭代器,为了简化迭代器的代码书写的
  • 所有单列集合和数组才能用增强for进行遍历
  • 在用增强for遍历时,也不能用集合的方法进行添加和删除
public class ForTest {
    public static void main(String[] args) {
        Collection<String> coll = new ArrayList<>();
        coll.add("xzq");
        coll.add("love");
        
        //快捷键:coll.for
        //s只是一个第三方变量,改变其值不会改变集合中原本的数据
        for(String s : coll){
            System.out.println(s);
        }
    }
}

Lambda表达式遍历

public class ForTest {
    public static void main(String[] args) {
        Collection<String> coll = new ArrayList<>();
        coll.add("xzq");
        coll.add("love");
        
        //利用匿名内部类
        //底层也是遍历集合,依次得到每一个元素
        //把得到的每一个元素传递给下面的accept方法
        //s表示集合中的每一个数据
        coll.forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
            //xzq
            //love
        });
        
        //Lambda表达式
        coll.forEach((String s) -> {
            System.out.println(s);
        });

        //简化写法,可以不写数据类型,且方法体只有一行
        coll.forEach(s -> System.out.println(s));
    }
}

List集合

  • 有序、有索引、可重复
  • Collection的方法List都继承了
  • List集合因为有索引,所以多了很多索引操作的方法
public class ListTest {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("xzq");
        list.add("love");
        list.add("you");
        System.out.println(list);//[xzq, love, you]

        //List集合有的在指定索引处添加元素
        list.add(1,"i");
        System.out.println(list);//[xzq, i, love, you]

        //返回被删除的元素
        System.out.println(list.remove(3));//you
        list.remove("xzq");
        System.out.println(list);//[i, love]

        //返回被替代的元素
        System.out.println(list.set(0,"qqq"));//i
        System.out.println(list);//[qqq, love]
        
        //获得指定索引处的元素
        System.out.println(list.get(1));//love

    }
}

在这里插入图片描述

public class IntRemoveTest {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        
        //remove方法有两种参数传递类型,见上面的图
        //默认是删除1索引的
        //本质:在调用方法的时候,如果方法出现了重载,会优先调用实参和形参一致的方法
        list.remove(1);

        //如果要删除Object的,需要先封装再传递
        //即可以删除值1
        Integer i = Integer.valueOf(1);
        list.remove(i);

        System.out.println(list);
    }
}

List系列集合的五种遍历方式:

  1. 迭代器(在遍历过程中需要删除元素)
  2. 列表迭代器(在遍历过程中需要添加元素)
  3. 增强for循环(只是想遍历)
  4. Lambda表达式遍历(只是想遍历)
  5. 普通for循环(遍历的时候想操作索引)
//列表迭代器
public class ListTest {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("xzq");
        list.add("love");
        list.add("you");

        //创建列表迭代器对象
        ListIterator<String> it = list.listIterator();
        
        //和Collection不同的是多了个add()方法
        while(it.hasNext()){
            String str = it.next();
            
            //remove()方法直接删去str元素
            if("love".equals(str)) {
                it.remove();
            }
            
            //add()方法在str元素后面一个位置加上新元素
            if("you".equals(str)){
                it.add("tt");
            }
        }
        System.out.println(list);//[xzq, you, tt]

    }
}

ArrayList集合

集合和数组对比:

  • 数组长度固定,集合长度可变
  • 数组可以存基本数据类型和引用数据类型,集合可以存引用数据类型,如果要存基本数据类型,要变成包装类后才可以存
public class Demo1 {
    public static void main(String[] args) {
        //集合是Java已经写好的一个类,底层做了处理,打印出来是里面的数据值,且会有[]将数据值包裹起来
        ArrayList<String> list = new ArrayList<>();
        System.out.println(list);
    }
}
[]

基本方法

public class Demo1 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        //增加操作
        //返回值是boolean,永远是true
        list.add("aaa");
        list.add("bbb");
        list.add("ccc");
        list.add("ddd");
        System.out.println(list);//[aaa, bbb, ccc, ddd]

        //删除操作
        //下面这个返回被删除的数据
        String s1 = list.remove(0);
        //下面这个若数据存在返回true,否则返回false
        boolean b1 = list.remove("ccc");
        System.out.println(s1);//aaa
        System.out.println(list);//[bbb, ddd]

        //修改操作,将对应索引的值更改
        //返回被修改的数据
        String s2 = list.set(0,"eee");
        System.out.println(s2);//bbb
        System.out.println(list);//[eee, ddd]

        //查询操作
        //返回被查询的数据
        String s3 = list.get(0);
        System.out.println(s3);//eee

        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
        //eee
        //ddd
    }
}
public class Demo3 {
    public static void main(String[] args) {
        //使用包装类
        ArrayList<Integer> list = new ArrayList<>();
        
        //int和Integer类型可以相互转化
        list.add(1);
        list.add(7);
        list.add(1);
        list.add(7);
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
    }
}
1
7
1
7
//调用Student类创建集合
public class StudentTest {
    public static void main(String[] args) {
        ArrayList<Student> list = new ArrayList<>();
        Scanner sc = new Scanner(System.in);
        for(int i = 0;i<3 ;i++){
            String name = sc.next();
            int age = sc.nextInt();
            Student s = new Student(name,age);
            list.add(s);
        }
        for (int i = 0; i < list.size(); i++) {
            System.out.print(list.get(i).getName() + ", " + list.get(i).getAge());
            System.out.println();
        }
    }
}
xzq 40
zrz 19
xxg 5
xzq, 40
zrz, 19
xxg, 5
 
//创建Student类
public class Student {
    private String name;
    private int age;

    public Student(){}
    public Student(String name,int age){
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
public class AddAllTest {
    public static void main(String[] args) {
        ArrayList<String> list1 = new ArrayList<>();
        list1.add("xzq");
        list1.add("love");

        ArrayList<String> list2 = new ArrayList<>();
        list2.add("i");
        
        //addAll()方法,将一个集合中全部元素加入另一个集合
        list2.addAll(list1);
        System.out.println(list2);

    }
}

ArrayList集合底层原理

  1. 利用空参创建的集合,在底层创建一个默认长度为0的数组
  2. 添加第一个元素时,底层会创建一个新的长度为10的数组
  3. 存满时,会扩容1.5倍
  4. 如果一次性添加多个元素,1.5倍还放不下,则新创建数组的长度以实际为准

泛型

  • 格式:<数据类型>
  • 泛型只能支持引用数据类型,如果要加int要写Integer
  • 泛型在编译时可以指定数据类型,但是在字节码文件中仍然是Object类型
//没泛型时,可以往集合加任何数据
public class ArrayListTest {
    public static void main(String[] args) {
        ArrayList list = new ArrayList();
        list.add(123);
        list.add("aaa");
        list.add(new Student("xzq",40));

        Iterator it = list.iterator();
        while(it.hasNext()){
            
            //多态的弊端是不能访问子类的特有功能
            //所以我们无法调用特有行为,强转也又得无法转
            //所以泛型很重要
            Object o = it.next();
            System.out.println(o);
        }
    }
}
123
aaa
collection.Student@4554617c

public class Student {
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age && Objects.equals(name, student.name);
    }
}

泛型类

当一个类中,某个变量的数据类型不确定时,就可以定义带有泛型的类。

//自己写一个泛型类,不确定数据类型,可以用任意大写字母表示,通常为E
public class MyArrayList<E> {
    Object[] obj = new Object[10];
    int size;

    public boolean add(E e){
        obj[size] = e;
        size++;
        return true;
    }

    public E get(int index){
        //要将Object类型强转为E类型
        return (E)obj[index];
    }

    @Override
    public String toString() {
        return Arrays.toString(obj);
    }
}

public class MyArrayListTest {
    public static void main(String[] args) {
        //此时E为String
        MyArrayList<String> list1 = new MyArrayList<>();
        list1.add("xzq");
        list1.add("love");
        System.out.println(list1);//[xzq, love, null, null, null, null, null, null, null, null]

        //此时E为Integer
        MyArrayList<Integer> list2 = new MyArrayList<>();
        list2.add(717);
        list2.add(1717);
        System.out.println(list2.get(1));//1717
        System.out.println(list2);//[717, 1717, null, null, null, null, null, null, null, null]
    }
}

泛型方法

方法中形参类型不确定时,可以使用类名后面定义的泛型(所有方法都能用),或者在方法上申明定义自己的泛型(只有本方法能用)。

//定义一个工具类
public class ListUtil {
    private ListUtil(){}

    //泛型方法
    //泛型放在修饰符后面
    //因为无法确定集合泛型故也要在方法前加泛型才能传入参数
    //E...e其实是传入一个数组,可以是任意长度
    public static<E> void addAll(ArrayList<E> list,E...e){
        for (int i = 0; i < e.length; i++) {
            list.add(e[i]);
        }
    }
}

public class ListUtilTest {
    public static void main(String[] args) {
        ArrayList<String> list1 = new ArrayList<>();
        ListUtil.addAll(list1,"xzq","i","love");
        System.out.println(list1);//[xzq, i, love]

        ArrayList<Integer> list2 = new ArrayList<>();
        ListUtil.addAll(list2,1017,1317,1717,17171);
        System.out.println(list2);//[1017, 1317, 1717, 171717]
    }
}

泛型接口

当一个接口中,某个变量的数据类型不确定时,就可以定义带有泛型的接口。

如何使用一个带泛型的接口:

  • 方法一:实现类给出具体类型
  • 方法二:实现类延续泛型,创建对象时再确定类型
//方法一:实现类给出具体类型
public class MyArrayList implements List<String> {
    //重写方法
}

public class MyArrayListTest {
    public static void main(String[] args) {
        //此时创建对象不用写泛型,因为已经确定了
        MyArrayList list = new MyArrayList();
    }
}

//方法二:实现类延续泛型,创建对象时再确定类型
public class MyArrayList<E> implements List<E> {
    //重写方法
}

public class MyArrayListTest {
    public static void main(String[] args) {
        //此时创建对象时需要确定类型
        MyArrayList<String> list = new MyArrayList();
    }
}
//泛型不具备继承性,但是数据具备继承性
public class Generics {
    public static void main(String[] args) {
        ArrayList<Ye> list1 = new ArrayList<>();
        ArrayList<Fu> list2 = new ArrayList<>();
        ArrayList<Zi> list3 = new ArrayList<>();
        
        //只能传list1,传list2和list3会报错
        method(list1);

        //数据有继承性,多态
        list1.add(new Ye());
        list1.add(new Fu());
        list1.add(new Zi());
    }

    public static void method(ArrayList<Ye> list){

    }
}

class Ye{}
class Fu extends Ye{}
class Zi extends Fu{}
//如果我们想传集合且集合里面是Ye或者Fu或者Zi
//可以使用泛型的通配符
//    ?也表示不确定的类型
//    他可以进行类型额限定
//    ? extends E:表示可以传递E或者E所有的子类类型
//    ? super E:表示可以传递E或者E所有的父类类型
public class Generics {
    public static void main(String[] args) {
        ArrayList<Ye> list1 = new ArrayList<>();
        ArrayList<Fu> list2 = new ArrayList<>();
        ArrayList<Zi> list3 = new ArrayList<>();
        method(list1);
        method(list2);
        method(list3);
    }

    //利用泛型的通配符
    public static void method(ArrayList<? extends Ye> list){

    }
}

class Ye{}
class Fu extends Ye{}
class Zi extends Fu{}

//应用场景
//1.如果我们在定义类、方法、接口的时候,如果类型不确定,就可以定义泛型、泛型方法、泛型接口。
//2.如果类型不确定,但是能知道以后只能传递某个继承体系中的,就可以使用泛型的通配符
//泛型的通配符关键点:可以限定泛型的范围

Set

Set接口中的方法基本上与Collection的API一致

  • HashSet:无序、不重复、无索引
  • LinkedHashSet:有序、不重复、无索引
  • TreeSet:可排序、不重复、无索引
public class SetTest {
    public static void main(String[] args) {
        Set<String> s = new HashSet<>();

        //Set系列集合不重复,若集合中元素不存在为true,反之为false
        s.add("张三");
        System.out.println(s.add("张三"));//false
        s.add("李四");
        s.add("王五");

        //Set系列集合是无序的
        System.out.println(s);//[李四, 张三, 王五]

        //迭代器遍历
        Iterator<String> it = s.iterator();
        while(it.hasNext()){
            String str = it.next();
            System.out.println(str);
        }

        //增强for遍历
        for (String str : s) {
            System.out.println(str);
        }

        //Lambda表达式遍历,匿名内部类
        s.forEach(new Consumer<String>() {
            @Override
            public void accept(String str) {
                System.out.println(str);
            }
        });

        //Lambda表达式遍历
        s.forEach(str -> System.out.println(str));
    }
}
李四
张三
王五

哈希值

  • 根据hashCode方法算出来的int类型的整数
  • 该方法定义在Object类中,所有对象都可以调用,默认使用地址值进行计算
  • 一般情况下,会重写hashCode方法,利用对象内部的属性值计算哈希值

对象的哈希值特点

  • 如果没有重写hashCode方法,不同对象计算出的哈希值是不同的
  • 如果已经重写HashCode方法,不同的对象只要属性值相同,计算出的哈希值就是一样的
  • 在小部分情况下,不同的属性值或者不同的地址值计算出来的哈希值也有可能一样(哈希碰撞)
public class HashTest {
    public static void main(String[] args) {
        Student s1 = new Student("xzq",40);
        Student s2 = new Student("xzq",40);
	
        //如果没有重写hashCode方法,不同对象计算出的哈希值是不同的
        //是根据地址值计算的
        System.out.println(s1.hashCode());//1163157884
        System.out.println(s2.hashCode());//1956725890
    }
}
public class Student {
    private String name;
    private int age;


    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age && Objects.equals(name, student.name);
    }

    //重写hashCode方法
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

public class HashTest {
    public static void main(String[] args) {
        Student s1 = new Student("xzq",40);
        Student s2 = new Student("xzq",40);

        //如果已经重写HashCode方法,不同的对象只要属性值相同,计算出的哈希值就是一样的
        System.out.println(s1.hashCode());//3696666
        System.out.println(s2.hashCode());//3696666
        
        //哈希碰撞
        System.out.println("abc".hashCode());//96354
        System.out.println("acD".hashCode());//96354
    }
}

HashSet底层原理

  • HashSet是集合底层采取哈希表存储数据
  • 哈希表是一种对于增删改查数据性能都较好的结构

哈希表组成

  • JDK8之前:数组+链表
  • JDK8开始:数组+链表+红黑树
  1. 创建一个默认长度16,默认加载因子为0.75(用于扩容)的数组,数组名为table

  2. 根据元素的哈希值跟数组的长度计算出应存入的位置

    int index = (数组长度 - 1) & 哈希值;

  3. 判断当前位置是否为null,如果是null直接存入

  4. 如果位置不为null,表示有元素,则调用equals方法比较属性值

  5. 一样:不存 不一样:存入数组,形成链表

    JDK8以前:新元素存入数组,老元素挂在新元素下面

    JDK8以后:新元素直接挂在老元素下面

  • JDK8以后,当链表长度超过8,而且数组长度大于等于64时,自动转换为红黑树
  • 如果集合中存储的是自定义对象,必须要重写hashCode和equals方法

你可能感兴趣的:(java,开发语言)