CMD常用命令

操作说明
盘符名称:盘符切换。E:回车,表示切换到E盘。
dir查看当前路径下的内容。
cd 目录进入单级目录。cd itheima
cd …回退到上一级目录。
cd 目录1\目录2…进入多级目录。cd itheima\JavaSE
cd |回退到盘符目录。
cls清屏。
exit退出命令提示符窗口。

JDK的安装目录介绍

目录名称说明
bin该路径下存放了JDK的各种工具命令。javac和java就放在这个目录。
conf该路径下存放了JDK的相关配置文件。
include该路径下存放了一些平台特定的头文件。
jmods该路径下存放了JDK的各种模块。
legal该路径下存放了JDK各模块的授权文档。
lib该路径下存放了JDK工具的一些补充JAR包。

idea项目结构

数据类型

注意点

  • 如果要定义 一个整数类型的变量,不知道选择哪种数据类型了,默认使用int。
  • 如果要定义 一个小数类型的变量,不知道选择哪种数据类型了,默认使用double。
  • 如果要定义一个long类型的变量,那么在数据值的后面需要加上L后缀。(大小写都可以,建议大写。)
  • 如果要定义一个float类型的变量,那么在数据值的后面需要加上F后缀。(大小写都可以)

键盘录入

//导包,其实就是先找到Scanner这个类在哪
import java.util.Scanner;
public class ScannerDemo1{
	public static void main(String[] args){
		//2.创建对象,其实就是申明一下,我准备开始用Scanner这个类了。
		Scanner sc = new Scanner(System.in);
		//3.接收数据
		//当程序运行之后,我们在键盘输入的数据就会被变量i给接收了
		System.out.println("请输入一个数字");
		int i = sc.nextInt();
		//String name = sc.next();//接受的数据类型为String
		System.out.println(i);
	}
}

输出拼接变量

"+变量名+"

字符的+操作

规则:

当+操作中出现了字符,会拿着字符到计算机内置的ASCII码表中去查对应的数字,然后再进行计算。

案例:

char c = 'a';  
int result = c + 0;  
System.out.println(result);//97

ASCII码表中: ‘a’ ----- 97 ‘A’ ----- 65

循环

package com.itheima.demo;  
import java.util.Scanner;  
public class demo1 {  
    public static void main(String[] args) {  
        Scanner sc = new Scanner(System.in);  
        System.out.println("请输入一个三位数");  
        int number=sc.nextInt();  
  
        int ones = number % 10;  
        int tens = number / 10 % 10;  
        int hundred = number / 100 % 10;  
  
        System.out.println(ones + "这是字符串拼接abc");  
        System.out.println(tens);  
        System.out.println(hundred);  
        String result = ones == tens ? "相同" : "不相同";  
        System.out.println(result);  
  
        if(ones == tens){  
            System.out.println("相等(if/else款)");  
        }else{  
            System.out.println("不相等(if/else款)");  
        }  
  
        int number1 = 10;  
        switch (number1) {//JDK12新特性  
            case 1 -> System.out.println("一");  
            case 2 -> System.out.println("二");  
            case 3 -> System.out.println("三");  
            default -> System.out.println("其他");  
        }  
    }  
}

数组

遍历:就是把数组里面所有的内容一个一个全部取出来。

数组的长度:数组名.length;

通用代码:

for(int i = 0; i < arr.length; i++){
    //在循环的过程中,i依次表示数组中的每一个索引
    sout(arr[i]);//就可以把数组里面的每一个元素都获取出来,并打印在控制台上了。
}

静态初始化

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

动态初始化

数据类型[] 数组名 = new 数据类型[数组的长度];
int [] ageArr = new int [10];

Random

Random跟Scanner一样,也是Java提前写好的类,我们不需要关心是如何实现的,只要直接使用就可以了。

使用步骤:

//1.导包
import java.util.Random;
 
public class RandomDemo1 {
    public static void main(String[] args) {
        //2.创建对象
        Random r = new Random();
        //3.生成随机数
        int number = r.nextInt(100);//包左不包右,包头不包尾
        //0 ~ 99
        System.out.println(number);
/*int number = r.nextInt(随机数的范围);
上面这个格式里面,只有number是变量名,可以变,其他的都不允许变。
随机数范围的特点:从0开始,不包含指定值。比如:参数为10,生成的范围[0,10)*/
    }
}

方法

无参

public static void 方法名 (){
	//方法体;
}

有参

public static void 方法名 (参数1,参数2 ){
 
}

带返回值的方法

public static 数据类型 方法名 (){
	return 数据;
}

重组

  1. 重载与返回值无关
public class MethodDemo {
	public static void fn(int a) {
    	//方法体
    }
    public static int fn(int a) { 	/*错误原因:重载与返回值无关*/
    	//方法体
    }
}
  1. 仅针对同一类方法的名字参数进行识别
public class MethodDemo01 {
    public static void fn(int a) {
        //方法体
    }
} 
public class MethodDemo02 {
    public static int fn(double a) { /*错误原因:这是两个类的两个fn方法*/
        //方法体
    }
}

println与print

输出内容并换行与输出内容不换行

ASCII表

ASCII码 - 基本ASCII码和扩展ASCII码,中文最全ASCII码对照表0~255 (asciim.cn)

A---65 a---97

面向对象

  • 属性:在类中通过成员变量来体现(类中方法外的变量)

  • 行为:在类中通过成员方法来体现(和前面的方法相比去掉static关键字即可

 
public class 类名 {
	// 成员变量
	变量1的数据类型 变量1;
	变量2的数据类型 变量2;

	// 成员方法
	方法1;
	方法2;	
	
	public class Phone {
    //成员变量
    String brand;
    int price;
 
    //成员方法
    public void call() {
        System.out.println("打电话");
    }
 
    public void sendMessage() {
        System.out.println("发短信");
    }
}

this关键字

  • this修饰的变量用于指代成员变量,其主要作用是(区分局部变量和成员变量的重名问题)

  • this出现在实例方法中,谁调用这个方法(哪个对象调用这个方法),this就代表谁(this就代表哪个对象)。

    • 方法的形参如果与成员变量同名,不带this修饰的变量指的是形参,而不是成员变量

    • 方法的形参没有与成员变量同名,不带this修饰的变量指的是成员变量

注意点:静态方法static 无this

public class Student {
    private String name;
    private int age;
 
    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 void show() {
        System.out.println(name + "," + age);
    }
}
 
public class StudentDemo {
    public static void main(String[] args) {
        //创建对象
        Student s = new Student();
        s.show();
    }

构造函数

私有成员变量的使用途径

  1. 有参构造就赋予其值

  2. 类内部的方法赋值

    构造方法放在类李

class Student {
    private String name;
    private int age;
 
    //无参构造方法
    public Student() {
        System.out.println("无参构造方法");
    }
    //有参构造(重载1)
    public Student(String name) {
        this.name = name;
    }
	//有参构造(重载2)
    public Student(int age) {
        this.age = age;
    }
 
 
    public void show() {
        System.out.println(name + "," + age);
    }
}
/*
    测试类
 */
public class StudentDemo {
    public static void main(String[] args) {
        //创建对象
        Student s = new Student();
        s.show();
    }
}
 

String

public class StringDemo01 {  
    public static void main(String[] args) {  
        //public String():创建一个空白字符串对象,不含有任何内容  
        String s1 = new String();  
        System.out.println("s1:" + s1);  
  
        //public String(char[] chs):根据字符数组的内容,来创建字符串对象  
        char[] chs = {'a', 'b', 'c'};  
        String s2 = new String(chs);  
        System.out.println("s2:" + s2);  
  
        //public String(byte[] bys):根据字节数组的内容,来创建字符串对象  
        byte[] bys = {97, 98, 99};  
        String s3 = new String(bys);  
        System.out.println("s3:" + s3);  
  
        //String s = “abc”; 直接赋值的方式创建字符串对象,内容就是abc  
        String s4 = "abc";  
        System.out.println("s4:" + s4);  
        //s1:  
        //s2:abc        //s3:abc        //s4:abc    }  
}

创建字符串对象两种方式的区别

  • 通过构造方法创建 通过 new 创建的字符串对象,每一次 new 都会申请一个内存空间,虽然内容相同,但是地址值不同

  • 直接赋值方式创建(最好) 以“”方式给出的字符串,只要字符序列相同(顺序和大小写),无论在程序代码中出现几次,JVM 都只会建立一个 String 对象,并在字符串池中维护

字符串的比较

==号的作用

equls方法的使用

  • 比较基本数据类型:比较的是具体的值

  • 比较引用数据类型:比较的是对象地址值

public boolean equals(String s);   //比较两个字符串内容是否相同、区分大小写
public boolean equalsIgnoreCase(s); //不区分大小写

遍历字符串案例

案例需求

键盘录入一个字符串,使用程序实现在控制台遍历该字符串

2.7.2直接遍历字符串

charAt方法的使用

public class Test2字符串直接遍历 {
    public static void main(String[] args) {
        //两个方法:
        //charAt():会根据索引获取对应的字符
        //length(): 会返回字符串的长度
 
 
        //1.键盘录入一个字符串
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入字符串");
        String str = sc.next();
        System.out.println(str);
 
        //2.遍历
        for (int i = 0; i < str.length(); i++) {
            //i 依次表示字符串的每一个索引
            //索引的范围:0 ~  长度-1
 
            //根据索引获取字符串里面的每一个字符
            //ctrl + alt + V 自动生成左边的接受变量
            char c = str.charAt(i);
            System.out.println(c);
        }
    }
}

字符串反转

public class Test6反转字符串 {
    public static void main(String[] args) {
        /*定义一个方法,实现字符串反转。键盘录入一个字符串,调用该方法后,在控制台输出结果
        例如,键盘录入 abc,输出结果 cba*/
 
 
 
        //1.定义一个字符串
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入一个字符串");
        String str = sc.next();
        //2.定义一个方法,反转字符串
        //abc  --->  cba
        //可以把字符串倒着遍历,再拼接
        String result = reverse(str);
        System.out.println(result);
 
 
    }
 
    //注释:方法的作用就是反转字符串
    //把传递进来的字符串进行反转
    public static String reverse(String str){//abc
        //核心思想:倒着遍历并进行拼接就可以了
        //fori :正着遍历  forr:倒着遍历
        String s = "";
        for (int i = str.length() - 1; i >= 0; i--) {
            //i 依次表示字符串里面的每一个索引(倒着的)
            //我们就可以拿到里面的每一个字符并拼接
            s = s + str.charAt(i);
        }
 
        //把倒着拼接之后的结果返回即可
        return s;
 
    }
}

2.12 手机号屏蔽

需求:以字符串的形式从键盘接受一个手机号,将中间四位号码屏蔽 subString方法的使用 最终效果为:131****9468

代码实现:

public class Test8手机号屏蔽 {
    public static void main(String[] args) {
        /*以字符串的形式从键盘接受一个手机号,将中间四位号码屏蔽
        最终效果为:131****9468*/
 
        //1.键盘录入一个手机号码
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入手机号码");
        String phoneNumber = sc.next();//13112349408
 
        //2.截取手机号码中的前三位
        // str.substring(begin, end)
        String star = phoneNumber.substring(0, 3);
 
        //3.截取手机号码中的最后四位
        //此时我用substring方法,是用1个参数的,还是两个参数的?1个参数的会更好
        //因为现在我要截取到最后,所以建议使用1个参数的。
        //返回一个字符串,该字符串是此字符串的子字符串。 子字符串以指定索引处的字符开头,并延伸到此字符串的末尾。
        String end = phoneNumber.substring(7);
 
        //4.拼接
        String result = star + "****" + end;
 
        System.out.println(result);
 
    }
}
 

2.13 敏感词替换

需求1:键盘录入一个 字符串,如果字符串中包含(TMD),则使用***替换 replace方法的使用

public class Test9敏感词替换 {
    public static void main(String[] args) {
        //1.定义一个变量表示骂人的话
        String talk = "后裔你玩什么啊,TMD";
 
 
        //2.把这句话中的敏感词进行替换
        //将此字符串中与文字目标序列匹配的每个子字符串替换为指定的文字替换序列。
        String result = talk.replace("TMD", "***");
 
        //3.打印
        System.out.println(talk);
        System.out.println(result);
    }
}
 

字符转数字

char gender = id.charAt(16);//'3'  ---> 3
        //利用ASCII码表进行转换
        //'0' --->  48
        //'1' --->  49
        //'2' --->  50
        //'3' --->  51
        //'4' --->  52
        //'5' --->  53
        //'6' --->  54
        //'7' --->  55
        //'8' --->  56
        //'9' --->  57
 
       int num = gender - 48;

StringBuilder

public class StringBuilderDemo3 {
    public static void main(String[] args) {
        //1.创建对象
        StringBuilder sb = new StringBuilder("abc");
 
        //2.添加元素
        /*sb.append(1);
        sb.append(2.3);
        sb.append(true);*/
 
        //反转
        sb.reverse();
 
        //获取长度
        int len = sb.length();
        System.out.println(len);
 
 
        //打印
        //普及:
        //因为StringBuilder是Java已经写好的类
        //java在底层对他做了一些特殊处理。
        //打印对象不是地址值而是属性值。
        System.out.println(sb);
    }
}

3.2 链式编程

SBuilder.toString方法的使用

public class StringBuilderDemo4 {
    public static void main(String[] args) {
        //1.创建对象
        StringBuilder sb = new StringBuilder();
 
        //2.添加字符串
        sb.append("aaa").append("bbb").append("ccc").append("ddd");
 
        System.out.println(sb);//aaabbbcccddd
 
        //3.再把StringBuilder变回字符串
        String str = sb.toString();
        System.out.println(str);//aaabbbcccddd
 
    }
}
package com.itheima.stringbuilderdemo;
 
public class StringBuilderDemo7 {
    public static void main(String[] args) {
        //1.定义数组
        int[] arr = {1,2,3};
 
        //2.调用方法把数组变成字符串
        String str = arrToString(arr);
 
        System.out.println(str);
 
    }
 
 
    public static String arrToString(int[] arr){
        StringBuilder sb = new StringBuilder();
        sb.append("[");
 
        for (int i = 0; i < arr.length; i++) {
            if(i == arr.length - 1){
                sb.append(arr[i]);
            }else{
                sb.append(arr[i]).append(", ");
            }
        }
        sb.append("]");
 
        return sb.toString();
    }
}

StringJoiner

  • StringJoiner跟StringBuilder一样,也可以看成是一个容器,创建之后里面的内容是可变的。

  • 作用:提高字符串的操作效率,而且代码编写特别简洁,但是目前市场上很少有人用。

  • JDK8出现的

基本使用:

//1.创建一个对象,并指定中间的间隔符号
StringJoiner sj = new StringJoiner("---");
//2.添加元素
sj.add("aaa").add("bbb").add("ccc");
//3.打印结果
System.out.println(sj);//aaa---bbb---ccc
 
//1.创建对象
StringJoiner sj = new StringJoiner(", ","[","]");
//2.添加元素
sj.add("aaa").add("bbb").add("ccc");
int len = sj.length();
System.out.println(len);//15
//3.打印
System.out.println(sj);//[aaa, bbb, ccc]
String str = sj.toString();
System.out.println(str);//[aaa, bbb, ccc]

ArrayList

1.2.1 构造方法

方法名说明
public ArrayList()创建一个空的集合对象

1.2.2 成员方法

方法名说明
public boolean add(要添加的元素)将指定的元素追加到此集合的末尾
public boolean remove(要删除的元素)删除指定元素,返回值表示是否删除成功
public E remove(int index)删除指定索引处的元素,返回被删除的元素
public E set(int index,E element)修改指定索引处的元素,返回被修改的元素
public E get(int index)返回指定索引处的元素
public int size()返回集合中的元素的个数
示例代码:
//创建集合
        ArrayList<String> array = new ArrayList<String>();
 
        //添加元素
        array.add("hello");
        array.add("world");
        array.add("java");
 

面向对象进阶

static

static可以修饰成员变量或者修饰方法

  • 被该类所有对象共享
  • 类名调用(推荐)或对象名调用

被static修饰的成员是属于类的是放在静态区(堆内存)中(公共成员!) 没有static修饰的成员变量和方法则是属于对象

static修饰的成员属于类,会存储在静态区,是随着类的加载而加载的,且只加载一次,所以只有一份,节省内存。存储于一块固定的内存区域(静态区),所以,可以直接被类名调用。它优先于对象存在,所以,可以被所有对象共享。

总结: 被static修饰的成员属于类,而没有被修饰属于实例对象(new出来的)

以上对静态方法同时适用

继承

通过 extends 关键字,可以声明一个子类继承另外一个父类,定义格式如下:

class 父类 {
	...
}
class 子类 extends 父类 {
	...
}

子类不能继承父类的构造方法。

值得注意的是子类可以继承父类的私有成员(成员变量,方法),只是子类无法直接访问而已,可以通过getter/setter方法访问父类的private成员变量。

继承中的同名变量

优先子类

子父类中出现了同名的成员变量时,在子类中需要访问父类中非私有成员变量时,需要使用super 关键字,修饰父类成员变量,类似于之前学过的 this

需要注意的是:super代表的是父类对象的引用,this代表的是当前对象的引用。

使用格式: super.父类成员变量名

class Fu {
	// Fu中的成员变量。
	int num = 5;
}
 
class Zi extends Fu {
	// Zi中的成员变量
	int num = 6;
  
	public void show() {
        int num = 1;
      
        // 访问方法中的num
        System.out.println("method num=" + num);
        // 访问子类中的num
        System.out.println("Zi num=" + this.num);
        // 访问父类中的num
        System.out.println("Fu num=" + super.num);
	}
}
 
class Demo04 {
	public static void main(String[] args) {
      	// 创建子类对象
		Zi1 z = new Zi1(); 
      	// 调用子类中的show方法
		z1.show(); 
	}
}
 
演示结果:
method num=1
Zi num=6
Fu num=5

方法重写

方法重写 :子类中出现与父类一模一样的方法时(返回值类型,方法名和参数列表都相同),会出现覆盖效果,也称为重写或者复写。声明不变,重新实现

发生在子父类之间的关系。 子类继承了父类的方法,但是子类觉得父类的这方法不足以满足自己的需求,子类重新写了一个与父类同名的方法,以便覆盖父类的该方 法。

  • @Override:注解,重写注解校验!
  • 这个注解标记的方法,就说明这个方法必须是重写父类的方法,否则编译阶段报错。
  • 建议重写都加上这个注解,一方面可以提高代码的可读性,一方面可以防止重写出错! 加上后的子类代码形式如下:
public class Cat extends Animal {
     // 声明不变,重新实现
    // 方法名称与父类全部一样,只是方法体中的功能重写写了!
    @Override
    public void cry(){
        System.out.println("我们一起学猫叫,喵喵喵!喵的非常好听!");
    }
}

继承中的构造函数

  1. 构造方法的名字是与类名一致的。所以子类是无法继承父类构造方法的。

  2. 构造方法的作用是初始化对象成员变量数据的。所以子类的初始化过程中,必须先执行父类的初始化动作。子类的构造方法中默认有一个super() ,表示调用父类的构造方法,父类成员变量初始化后,才可以给子类使用。(先有爸爸,才能有儿子

继承后子类构方法器特点:子类所有构造方法的第一行都会默认先调用父类的无参构造方法

class Person {
    private String name;
    private int age;
 
    public Person() {
        System.out.println("父类无参");
    }
 
    // getter/setter省略
}
 
class Student extends Person {
    private double score;
 
    public Student() {
        //super(); // 调用父类无参,默认就存在,可以不写,必须再第一行
        System.out.println("子类无参");
    }
    
     public Student(double score) {
        //super();  // 调用父类无参,默认就存在,可以不写,必须再第一行
        this.score = score;    
        System.out.println("子类有参");
     }
 
}
 
public class Demo07 {
    public static void main(String[] args) {
        Student s1 = new Student();
        System.out.println("----------");
        Student s2 = new Student(99.9);
    }
}
 
输出结果:
父类无参
子类无参
----------
父类无参
子类有参

小结

  • 子类构造方法执行的时候,都会在第一行默认先调用父类无参数构造方法一次。
  • 子类构造方法的第一行都隐含了一个super() 去调用父类无参数构造方法,super() 可以省略不写。

super和this完整的用法如下,其中this,super访问成员我们已经接触过了。

this.成员变量    	--    本类的
super.成员变量    	--    父类的
 
this.成员方法名()  	--    本类的    
super.成员方法名()   --    父类的

接下来我们使用调用构造方法格式:

super(...) -- 调用父类的构造方法,根据参数匹配确认
this(...) -- 调用本类的其他构造方法,根据参数匹配确认

以下为代码示例:(super)

class Person {
    private String name ="凤姐";
    private int age = 20;
 
    public Person() {
        System.out.println("父类无参");
    }
    
    public Person(String name , int age){
        this.name = name ;
        this.age = age ;
    }
 
    // getter/setter省略
}
 
class Student extends Person {
    private double score = 100;
 
    public Student() {
        //super(); // 调用父类无参构造方法,默认就存在,可以不写,必须再第一行
        System.out.println("子类无参");
    }
    
     public Student(String name , int age,double score) {
        super(name ,age);// 调用父类有参构造方法Person(String name , int age)初始化name和age
        this.score = score;    
        System.out.println("子类有参");
     }
      // getter/setter省略
}
 
public class Demo07 {
    public static void main(String[] args) {
        // 调用子类有参数构造方法
        Student s2 = new Student("张三",20,99);
        System.out.println(s2.getScore()); // 99
        System.out.println(s2.getName()); // 输出 张三
        System.out.println(s2.getAge()); // 输出 20
    }
}

以下为代码示例(this)

package com.itheima._08this和super调用构造方法;
/**
 * this(...):
 *    默认是去找本类中的其他构造方法,根据参数来确定具体调用哪一个构造方法。
 *    为了借用其他构造方法的功能。
 *
 */
public class ThisDemo01 {
    public static void main(String[] args) {
        Student xuGan = new Student();
        System.out.println(xuGan.getName()); // 输出:徐干
        System.out.println(xuGan.getAge());// 输出:21
        System.out.println(xuGan.getSex());// 输出: 男
    }
}
 
class Student{
    private String name ;
    private int age ;
    private char sex ;
 
    public Student() {
  // 很弱,我的兄弟很牛逼啊,我可以调用其他构造方法:Student(String name, int age, char sex)
        this("徐干",21,'男'); 
    }
 
    public Student(String name, int age, char sex) {
        this.name = name ;
        this.age = age   ;
        this.sex = sex   ;
    }
 
//省略getter/setter

多态

  • 当一个方法的形参是一个类,我们可以传递这个类所有的子类对象。
  • 当一个方法的形参是一个接口,我们可以传递这个接口所有的实现类对象(后面会学)。
  • 而且多态还可以根据传递的不同对象来调用不同类中的方法。
父类:
public class Person {
    private String name;
    private int age;
 
    空参构造
    带全部参数的构造
    get和set方法
 
    public void show(){
        System.out.println(name + ", " + age);
    }
}
 
子类1:
public class Administrator extends Person {
    @Override
    public void show() {
        System.out.println("管理员的信息为:" + getName() + ", " + getAge());
    }
}
 
子类2:
public class Student extends Person{
 
    @Override
    public void show() {
        System.out.println("学生的信息为:" + getName() + ", " + getAge());
    }
}
 
子类3:
public class Teacher extends Person{
 
    @Override
    public void show() {
        System.out.println("老师的信息为:" + getName() + ", " + getAge());
    }
}
 
测试类:
public class Test {
    public static void main(String[] args) {
        //创建三个对象,并调用register方法
 
        Student s = new Student();
        s.setName("张三");
        s.setAge(18);
 
 
        Teacher t = new Teacher();
        t.setName("王建国");
        t.setAge(30);
 
        Administrator admin = new Administrator();
        admin.setName("管理员");
        admin.setAge(35);
 
 
 
        register(s);
        register(t);
        register(admin);
 
 
    }
 
 
 
    //这个方法既能接收老师,又能接收学生,还能接收管理员
    //只能把参数写成这三个类型的父类
    public static void register(Person p){
        p.show();
    }
}

多态: 是指同一行为,具有多个不同表现形式。

从上面案例可以看出,Cat和Dog都是动物,都是吃这一行为,但是出现的效果(表现形式)是不一样的。

前提【重点】

  1. 有继承或者实现关系
  2. 方法的重写【意义体现:不重写,无意义】
  3. 父类引用指向子类对象【格式体现】

    父类类型:指子类对象继承的父类类型,或者实现的父接口类型。

1.4 多态的运行特点

调用成员变量时:编译看左边,运行看左边 调用成员方法时:编译看左边,运行看右边 代码示例:

Fu f = new Zi();
//编译看左边的父类中有没有name这个属性,没有就报错
//在实际运行的时候,把父类name属性的值打印出来
System.out.println(f.name);
//编译看左边的父类中有没有show这个方法,没有就报错
//在实际运行的时候,运行的是子类中的show方法
f.show();

多态的弊端

class Animal{
    public  void eat(){
        System.out.println("动物吃东西!")

}
class Cat extends Animal {  
    public void eat() {  
        System.out.println("吃鱼");  
    }  
   
    public void catchMouse() {  
        System.out.println("抓老鼠");  
    }  
}  
 
class Dog extends Animal {  
    public void eat() {  
        System.out.println("吃骨头");  
    }  
}
 
class Test{
    public static void main(String[] args){
        Animal a = new Cat();
        a.eat();
        a.catchMouse();//编译报错,编译看左边,Animal没有这个方法
    }
}

多态的转换

1.6.2 向上转型(自动转换)

  • 向上转型:多态本身是子类类型向父类类型向上转换(自动转换)的过程,这个过程是默认的。 当父类引用指向一个子类对象时,便是向上转型。 使用格式:
父类类型  变量名 = new 子类类型();  
如:Animal a = new Cat();

**原因是:父类类型相对与子类来说是大范围的类型,Animal是动物类,是父类类型。Cat是猫类,是子类类型。Animal类型的范围当然很大,包含一切动物。**所以子类范围小可以直接自动转型给父类类型的变量。

1.6.3 向下转型(强制转换)

  • 向下转型:父类类型向子类类型向下转换的过程,这个过程是强制的。 一个已经向上转型的子类对象,将父类引用转为子类引用,可以使用强制类型转换的格式,便是向下转型。

使用格式:

子类类型 变量名 = (子类类型) 父类变量名;  
:Aniaml a = new Cat();  
   Cat c =(Cat) a;  

1.6.4 案例演示

当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误。也就是说,不能调用子类拥有,而父类没有的方法。编译都错误,更别说运行了。这也是多态给我们带来的一点”小麻烦”。所以,想要调用子类特有的方法,必须做向下转型。

转型演示,代码如下:

定义类:

abstract class Animal {  
    abstract void eat();  
}  
 
class Cat extends Animal {  
    public void eat() {  
        System.out.println("吃鱼");  
    }  
    public void catchMouse() {  
        System.out.println("抓老鼠");  
    }  
}  
 
class Dog extends Animal {  
    public void eat() {  
        System.out.println("吃骨头");  
    }  
    public void watchHouse() {  
        System.out.println("看家");  
    }  
}

测试类:

public class Test {
    public static void main(String[] args) {
        // 向上转型  
        Animal a = new Cat();  
        a.eat(); 				// 调用的是 Cat 的 eat
 
        // 向下转型  
        Cat c = (Cat)a;       
        c.catchMouse(); 		// 调用的是 Cat 的 catchMouse
    }  
}

转换的异常

public class Test {
    public static void main(String[] args) {
        // 向上转型  
        Animal a = new Cat();  
        a.eat();               // 调用的是 Cat 的 eat
 
        // 向下转型  
        Dog d = (Dog)a;       
        d.watchHouse();        // 调用的是 Dog 的 watchHouse 【运行报错】
    }  
}
//报出了 `ClassCastException` ,类型转换异常!这是因为,明明创建了Cat类型对象,运行时,当然不能转换成Dog对象的。

1.6.6 instanceof关键字

public class Test {
    public static void main(String[] args) {
        // 向上转型  
        Animal a = new Cat();  
        a.eat();               // 调用的是 Cat 的 eat
 
        // 向下转型  
        if (a instanceof Cat){
            Cat c = (Cat)a;       
            c.catchMouse();        // 调用的是 Cat 的 catchMouse
        } else if (a instanceof Dog){
            Dog d = (Dog)a;       
            d.watchHouse();       // 调用的是 Dog 的 watchHouse
        }
    }  
}

1.6.7 instanceof新特性

JDK14的时候提出了新特性,把判断和强转合并成了一行

//新特性
//先判断a是否为Dog类型,如果是,则强转成Dog类型,转换之后变量名为d
//如果不是,则不强转,结果直接是false
if(a instanceof Dog d){
    d.lookHome();
}else if(a instanceof Cat c){
    c.catchMouse();
}else{
    System.out.println("没有这个类型,无法转换");
}

综合实例

//动物类(父类)
public class Animal {
    private int age;
    private String color;
 
 
    public Animal() {
    }
 
    public Animal(int age, String color) {
        this.age = age;
        this.color = color;
    }
 
    public int getAge() {
        return age;
    }
 
    public void setAge(int age) {
        this.age = age;
    }
 
    public String getColor() {
        return color;
    }
 
    public void setColor(String color) {
        this.color = color;
    }
 
    public void eat(String something){
        System.out.println("动物在吃" + something);
    }
}
 
//猫类(子类)
public class Cat extends Animal {
 
    public Cat() {
    }
 
    public Cat(int age, String color) {
        super(age, color);
    }
 
    @Override
    public void eat(String something) {
        System.out.println(getAge() + "岁的" + getColor() + "颜色的猫眯着眼睛侧着头吃" + something);
    }
 
    public void catchMouse(){
        System.out.println("猫抓老鼠");
    }
 
}
 
//狗类(子类)
public class Dog extends Animal {
    public Dog() {
    }
 
    public Dog(int age, String color) {
        super(age, color);
    }
 
    //行为
    //eat(String something)(something表示吃的东西)
    //看家lookHome方法(无参数)
    @Override
    public void eat(String something) {
        System.out.println(getAge() + "岁的" + getColor() + "颜色的狗两只前腿死死的抱住" + something + "猛吃");
    }
 
    public void lookHome(){
        System.out.println("狗在看家");
    }
}
 
 
//饲养员类
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 keepPet(Dog dog, String something) {
        System.out.println("年龄为" + age + "岁的" + name + "养了一只" + dog.getColor() + "颜色的" + dog.getAge() + "岁的狗");
        dog.eat(something);
    }
 
    //饲养猫
    public void keepPet(Cat cat, String something) {
        System.out.println("年龄为" + age + "岁的" + name + "养了一只" + cat.getColor() + "颜色的" + cat.getAge() + "岁的猫");
        cat.eat(something);
    }*/
 
 
    //想要一个方法,能接收所有的动物,包括猫,包括狗
    //方法的形参:可以写这些类的父类 Animal
    public void keepPet(Animal a, String something) {
        if(a instanceof Dog d){
            System.out.println("年龄为" + age + "岁的" + name + "养了一只" + a.getColor() + "颜色的" + a.getAge() + "岁的狗");
            d.eat(something);
        }else if(a instanceof Cat c){
            System.out.println("年龄为" + age + "岁的" + name + "养了一只" + c.getColor() + "颜色的" + c.getAge() + "岁的猫");
            c.eat(something);
        }else{
            System.out.println("没有这种动物");
        }
    }
}
 
//测试类
public class Test {
    public static void main(String[] args) {
        //创建对象并调用方法
       /* Person p1 = new Person("老王",30);
        Dog d = new Dog(2,"黑");
        p1.keepPet(d,"骨头");
 
 
        Person p2 = new Person("老李",25);
        Cat c = new Cat(3,"灰");
        p2.keepPet(c,"鱼");*/
 
 
        //创建饲养员的对象
        Person p = new Person("老王",30);
        Dog d = new Dog(2,"黑");
        Cat c = new Cat(3,"灰");
        p.keepPet(d,"骨头");
        p.keepPet(c,"鱼");
 
    }
}

导包

使用不同包下的相同类怎么办?

假设demo1和demo2中都有一个Student该如何使用?

代码示例:

//使用全类名的形式即可。
//全类名:包名 + 类名
//拷贝全类名的快捷键:选中类名crtl + shift + alt + c 或者用鼠标点copy,再点击copy Reference
com.itheima.homework.demo1.Student s1 = new com.itheima.homework.demo1.Student();
com.itheima.homework.demo2.Student s2 = new com.itheima.homework.demo2.Student();

权限修饰符

publicprotected默认private
同一类中
同一包中的类
不同包的子类
不同包中的无关类

final关键字

学习了继承后,我们知道,子类可以在父类的基础上改写父类内容,比如,方法重写。

如果有一个方法我不想别人去改写里面内容,该怎么办呢?

Java提供了final 关键字,表示修饰的内容不可变。

  • final: 不可改变,最终的含义。可以用于修饰类、方法和变量。

    • 类:被修饰的类,不能被继承。

    • 方法:被修饰的方法,不能被重写。

    • 变量:被修饰的变量,有且仅能被赋值一次。

抽象类

1.2 abstract使用格式

abstract是抽象的意思,用于修饰方法方法和类,修饰的方法是抽象方法,修饰的类是抽象类。

1.2.1 抽象方法

使用abstract 关键字修饰方法,该方法就成了抽象方法,抽象方法只包含一个方法名,而没有方法体。

定义格式:

修饰符 abstract 返回值类型 方法名 (参数列表);

代码举例:

public abstract void run();

1.2.2 抽象类

如果一个类包含抽象方法,那么该类必须是抽象类。注意:抽象类不一定有抽象方法,但是有抽象方法的类必须定义成抽象类。

定义格式:

 
abstract class 类名字 {   
    
}

代码举例:

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

1.2.3 抽象类的使用

要求:继承抽象类的子类必须重写父类所有的抽象方法。否则,该子类也必须声明为抽象类。

代码举例:

// 父类,抽象类
abstract class Employee {
	private String id;
	private String name;
	private double salary;
	
	public Employee() {
	}
	
	public Employee(String id, String name, double salary) {
		this.id = id;
		this.name = name;
		this.salary = salary;
	}
	
	// 抽象方法
	// 抽象方法必须要放在抽象类中
	abstract public void work();
}
 
// 定义一个子类继承抽象类
class Manager extends Employee {
	public Manager() {
	}
	public Manager(String id, String name, double salary) {
		super(id, name, salary);
	}
	// 2.重写父类的抽象方法
	@Override
	public void work() {
		System.out.println("管理其他人");
	}
}
 
// 定义一个子类继承抽象类
class Cook extends Employee {
	public Cook() {
	}
	public Cook(String id, String name, double salary) {
		super(id, name, salary);
	}
	@Override
	public void work() {
		System.out.println("厨师炒菜多加点盐...");
	}
}
 
// 测试类
public class Demo10 {
	public static void main(String[] args) {
		// 创建抽象类,抽象类不能创建对象
		// 假设抽象类让我们创建对象,里面的抽象方法没有方法体,无法执行.所以不让我们创建对象
//		Employee e = new Employee();
//		e.work();
		
		// 3.创建子类
		Manager m = new Manager();
		m.work();
		
		Cook c = new Cook("ap002", "库克", 1);
		c.work();
	}
}

此时的方法重写,是子类对父类抽象方法的完成实现,我们将这种方法重写的操作,也叫做实现方法

1.3 抽象类的特征

抽象类的特征总结起来可以说是 有得有失

有得:抽象类得到了拥有抽象方法的能力。

有失:抽象类失去了创建对象的能力。

其他成员(构造方法,实例方法,静态方法等)抽象类都是具备的。

1.4 抽象类的细节

不需要背,只要当idea报错之后,知道如何修改即可。

关于抽象类的使用,以下为语法上要注意的细节,虽然条目较多,但若理解了抽象的本质,无需死记硬背。

  1. 抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象。

    理解:假设创建了抽象类的对象,调用抽象的方法,而抽象方法没有具体的方法体,没有意义。

  2. 抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的。

    理解:子类的构造方法中,有默认的super(),需要访问父类构造方法。

  3. 抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。

    理解:未包含抽象方法的抽象类,目的就是不想让调用者创建该类对象,通常用于某些特殊的类结构设计。

  4. 抽象类的子类,必须重写抽象父类中所有的抽象方法,否则子类也必须定义成抽象类,编译无法通过而报错。

    理解:假设不重写所有抽象方法,则类中可能包含抽象方法。那么创建对象后,调用抽象的方法,没有意义。

  5. 抽象类存在的意义是为了被子类继承。

    理解:抽象类中已经实现的是模板中确定的成员,抽象类不确定如何实现的定义成抽象方法,交给具体的子类去实现。

第二章 接口

2.1 概述

我们已经学完了抽象类,抽象类中可以用抽象方法,也可以有普通方法,构造方法,成员变量等。那么什么是接口呢?接口是更加彻底的抽象,JDK7之前,包括JDK7,接口中全部是抽象方法。接口同样是不能创建对象的

2.2 定义格式

//接口的定义格式:  
interface 接口名称{  
    // 抽象方法  
}  

// 接口的声明:interface  
// 接口名称:首字母大写,满足“驼峰模式”

2.3 接口成分的特点

在JDK7,包括JDK7之前,接口中的只有包含:抽象方法和常量

2.3.1.抽象方法

注意:接口中的抽象方法默认会自动加上public abstract修饰程序员无需自己手写!! ​ 按照规范:以后接口中的抽象方法建议不要写上public abstract。因为没有必要啊,默认会加上。

2.3.2 常量

在接口中定义的成员变量默认会加上: public static final修饰。也就是说在接口中定义的成员变量实际上是一个常量。这里是使用public static final修饰后,变量值就不可被修改,并且是静态化的变量可以直接用接口名访问,所以也叫常量。常量必须要给初始值。常量命名规范建议字母全部大写,多个单词用下划线连接。

2.3.3 案例演示

public interface InterF {
    // 抽象方法!
    //    public abstract void run();
    void run();
 
    //    public abstract String getName();
    String getName();
 
    //    public abstract int add(int a , int b);
    int add(int a , int b);
 
 
    // 它的最终写法是:
    // public static final int AGE = 12 ;
    int AGE  = 12; //常量
    String SCHOOL_NAME = "黑马程序员";
 
}

2.4 基本的实现

2.4.1 实现接口的概述

类与接口的关系为实现关系,即类实现接口,该类可以称为接口的实现类,也可以称为接口的子类。实现的动作类似继承,格式相仿,只是关键字不同,实现使用 implements关键字。

2.4.2 实现接口的格式

/**接口的实现:
    在Java中接口是被实现的,实现接口的类称为实现类。
    实现类的格式:*/
class 类名 implements 接口1,接口2,接口3...{
 
}

从上面格式可以看出,接口是可以被多实现的。大家可以想一想为什么呢?

2.4.3 类实现接口的要求和意义

  1. 必须重写实现的全部接口中所有抽象方法。
  2. 如果一个类实现了接口,但是没有重写完全部接口的全部抽象方法,这个类也必须定义成抽象类。
  3. 意义:接口体现的是一种规范,接口对实现类是一种强制性的约束,要么全部完成接口申明的功能,要么自己也定义成抽象类。这正是一种强制性的规范。

2.4.4 类与接口基本实现案例

假如我们定义一个运动员的接口(规范),代码如下:

/**
   接口:接口体现的是规范。
 * */
public interface SportMan {
    void run(); // 抽象方法,跑步。
    void law(); // 抽象方法,遵守法律。
    String compittion(String project);  // 抽象方法,比赛。
}

接下来定义一个乒乓球运动员类,实现接口,实现接口的实现类代码如下:

package com.itheima._03接口的实现;
/**
 * 接口的实现:
 *    在Java中接口是被实现的,实现接口的类称为实现类。
 *    实现类的格式:
 *      class 类名 implements 接口1,接口2,接口3...{
 *
 *
 *      }
 * */
public class PingPongMan  implements SportMan {
    @Override
    public void run() {
        System.out.println("乒乓球运动员稍微跑一下!!");
    }
 
    @Override
    public void law() {
        System.out.println("乒乓球运动员守法!");
    }
 
    @Override
    public String compittion(String project) {
        return "参加"+project+"得金牌!";
    }
}

测试代码

public class TestMain {
    public static void main(String[] args) {
        // 创建实现类对象。
        PingPongMan zjk = new PingPongMan();
        zjk.run();
        zjk.law();
        System.out.println(zjk.compittion("全球乒乓球比赛"));
 
    }
}

2.4.5 类与接口的多实现案例

类与接口之间的关系是多实现的,一个类可以同时实现多个接口。

首先我们先定义两个接口,代码如下:

/** 法律规范:接口*/  
public interface Law {  
    void rule();  
}  
​  
/** 这一个运动员的规范:接口*/  
public interface SportMan {  
    void run();  
}  
​

然后定义一个实现类:

/**  
 * Java中接口是可以被多实现的:  
 *    一个类可以实现多个接口: Law, SportMan  
 *  
 * */  
public class JumpMan implements Law ,SportMan {  
    @Override  
    public void rule() {  
        System.out.println("尊长守法");  
    }  
​  
    @Override  
    public void run() {  
        System.out.println("训练跑步!");  
    }  
}

从上面可以看出类与接口之间是可以多实现的,我们可以理解成实现多个规范,这是合理的。

2.5 接口与接口的多继承

Java中,接口与接口之间是可以多继承的:也就是一个接口可以同时继承多个接口。大家一定要注意: 类与接口是实现关系 接口与接口是继承关系 接口继承接口就是把其他接口的抽象方法与本接口进行了合并。

案例演示:

public interface Abc {  
    void go();  
    void test();  
}  

/** 法律规范:接口*/  
public interface Law {  
    void rule();  
    void test();  
}  

 *  
 *  总结:  
 *     接口与类之间是多实现的。  
 *     接口与接口之间是多继承的。  
 * */  
public interface SportMan extends Law , Abc {  
    void run();  
}

2.6扩展:接口的细节

不需要背,只要当idea报错之后,知道如何修改即可 关于接口的使用,以下为语法上要注意的细节,虽然条目较多,但若理解了抽象的本质,无需死记硬背。

  1. 当两个接口中存在相同抽象方法的时候,该怎么办?

只要重写一次即可。此时重写的方法,既表示重写1接口的,也表示重写2接口的。

  1. 实现类能不能继承A类的时候,同时实现其他接口呢?

继承的父类,就好比是亲爸爸一样 实现的接口,就好比是干爹一样 可以继承一个类的同时,再实现多个接口,只不过,要把接口里面所有的抽象方法,全部实现。

  1. 实现类能不能继承一个抽象类的时候,同时实现其他接口呢?

实现类可以继承一个抽象类的同时,再实现其他多个接口,只不过要把里面所有的抽象方法全部重写。

  1. 实现类Zi,实现了一个接口,还继承了一个Fu类。假设在接口中有一个方法,父类中也有一个相同的方法。子类如何操作呢?

处理办法一:如果父类中的方法体,能满足当前业务的需求,在子类中可以不用重写。 处理办法二:如果父类中的方法体,不能满足当前业务的需求,需要在子类中重写。

  1. 如果一个接口中,有10个抽象方法,但是我在实现类中,只需要用其中一个,该怎么办?

可以在接口跟实现类中间,新建一个中间类(适配器类) 让这个适配器类去实现接口,对接口里面的所有的方法做空重写。 让子类继承这个适配器类,想要用到哪个方法,就重写哪个方法。 因为中间类没有什么实际的意义,所以一般会把中间类定义为抽象的,不让外界创建对象

\

第三章 内部类

3.1 概述

3.1.1 什么是内部类

将一个类A定义在另一个类B里面,里面的那个类A就称为内部类,B则称为外部类。可以把内部类理解成寄生,外部类理解成宿主。

3.1.2 什么时候使用内部类

一个事物内部还有一个独立的事物,内部的事物脱离外部的事物无法独立使用

  1. 人里面有一颗心脏。
  2. 汽车内部有一个发动机。
  3. 为了实现更好的封装性。

3.2 内部类的分类

按定义的位置来分

  1. 成员内部类,类定义在了成员位置 (类中方法外称为成员位置,无static修饰的内部类) 静态内部类:类定义在了成员位置 (类中,方法外称为成员位置,有static修饰的内部类)(被static修饰的成员内部类)

  2. 局部内部类,类定义在方法内

  3. 匿名内部类,没有名字的内部类,可以在方法中,也可以在类中方法外。

3.3 成员内部类

成员内部类特点

  • 无static修饰的内部类,属于外部类对象的。
  • 宿主:外部类对象。

内部类的使用格式

外部类.内部类。 // 访问内部类的类型都是用 外部类.内部类

获取成员内部类对象的两种方式

方式一:外部直接创建成员内部类的对象 外部类.内部类 变量 = new 外部类().new 内部类(); 方式二:在外部类中定义一个方法提供内部类的对象

案例演示

方式一:

public class Test {  
    public static void main(String[] args) {  
        //  宿主:外部类对象。  
       // Outer out = new Outer();  
        // 创建内部类对象。  
        Outer.Inner oi = new Outer().new Inner();  
        oi.method();  
    }  
}  

class Outer {  
    // 成员内部类,属于外部类对象的。  
    // 拓展:成员内部类不能定义静态成员。  
    public class Inner{  
        // 这里面的东西与类是完全一样的。  
        public void method(){  
            System.out.println("内部类中的方法被调用了");  
        }  
    }  
}  


方式二:  
public class Outer {  
    String name;  
    private class Inner{  
        static int a = 10;  
    }  
    public Inner getInstance(){  
        return new Inner();  
    }  
}  

public class Test {  
    public static void main(String[] args) {  
        Outer o = new Outer();  
        System.out.println(o.getInstance());  


    }  
}

3.4 成员内部类的细节

编写成员内部类的注意点:

  1. 成员内部类可以被一些修饰符所修饰,比如: private,默认,protected,public,static等
  2. 在成员内部类里面,JDK16之前不能定义静态变量,JDK16开始才可以定义静态变量。
  3. 创建内部类对象时,对象中有一个隐含的Outer.this记录外部类对象的地址值。(请参见3.6节的内存图)

详解:

内部类被private修饰,外界无法直接获取内部类的对象,只能通过3.3节中的方式二获取内部类的对象 被其他权限修饰符修饰的内部类一般用3.3节中的方式一直接获取内部类的对象 内部类被static修饰是成员内部类中的特殊情况,叫做静态内部类下面单独学习。 内部类如果想要访问外部类的成员变量,外部类的变量必须用final修饰,JDK8以前必须手动写final,JDK8之后不需要手动写,JDK默认加上。

3.5 成员内部类面试题

请在?地方向上相应代码,以达到输出的内容 注意:内部类访问外部类对象的格式是:外部类名.this

public class Test {  
    public static void main(String[] args) {  
        Outer.inner oi = new Outer().new inner();  
        oi.method();  
    }  
}  

class Outer {   // 外部类  
    private int a = 30;  

    // 在成员位置定义一个类  
    class inner {  
        private int a = 20;  

        public void method() {  
            int a = 10;  
            System.out.println(???);    // 10   答案:a  
            System.out.println(???);    // 20   答案:this.a  
            System.out.println(???);    // 30   答案:Outer.this.a  
        }  
    }  
}

3.6 成员内部类内存图

内部类内存图

3.7 静态内部类

静态内部类特点

  • 静态内部类是一种特殊的成员内部类。

  • 有static修饰,属于外部类本身的。

  • 总结:静态内部类与其他类的用法完全一样。只是访问的时候需要加上外部类.内部类。

  • 拓展1:静态内部类可以直接访问外部类的静态成员。

  • 拓展2:静态内部类不可以直接访问外部类的非静态成员,如果要访问需要创建外部类的对象。

  • 拓展3:静态内部类中没有银行的Outer.this。

内部类的使用格式

外部类.内部类 变量

静态内部类对象的创建格式

外部类.内部类  变量 = new  外部类.内部类构造器;

调用方法的格式:

  • 调用非静态方法的格式:先创建对象,用对象调用

  • 调用静态方法的格式:外部类名.内部类名.方法名();

案例演示

// 外部类:Outer01  
class Outer01{  
    private static  String sc_name = "黑马程序";  
    // 内部类: Inner01  
    public static class Inner01{  
        // 这里面的东西与类是完全一样的。  
        private String name;  
        public Inner01(String name) {  
            this.name = name;  
        }  
        public void showName(){  
            System.out.println(this.name);  
            // 拓展:静态内部类可以直接访问外部类的静态成员。  
            System.out.println(sc_name);  
        }  
    }  
}  

public class InnerClassDemo01 {  
    public static void main(String[] args) {  
        // 创建静态内部类对象。  
        // 外部类.内部类  变量 = new  外部类.内部类构造器;  
        Outer01.Inner01 in  = new Outer01.Inner01("张三");  
        in.showName();  
    }  
}

3.8 局部内部类

  • 局部内部类 :定义在方法中的类。

定义格式:

class 外部类名 {  
    数据类型 变量名;  
      
    修饰符 返回值类型 方法名(参数列表) {  
        // …  
        class 内部类 {  
            // 成员变量  
            // 成员方法  
        }  
    }  
}

3.9 匿名内部类【重点】

3.9.1 概述

匿名内部类 :是内部类的简化写法。他是一个隐含了名字的内部类。开发中,最常用到的内部类就是匿名内部类了。

3.9.2 格式

new 类名或者接口名() {  
     重写方法;  
};

包含了:

  • 继承或者实现关系
  • 方法重写
  • 创建对象

所以从语法上来讲,这个整体其实是匿名内部类对象

3.9.2 什么时候用到匿名内部类

实际上,如果我们希望定义一个只要使用一次的类,就可考虑使用匿名内部类。匿名内部类的本质作用

是为了简化代码

之前我们使用接口时,似乎得做如下几步操作:

  1. 定义子类
  2. 重写接口中的方法
  3. 创建子类对象
  4. 调用重写后的方法
interface Swim {  
    public abstract void swimming();  
}  

// 1. 定义接口的实现类  
class Student implements Swim {  
    // 2. 重写抽象方法  
    @Override  
    public void swimming() {  
        System.out.println("狗刨式...");  
    }  
}  

public class Test {  
    public static void main(String[] args) {  
        // 3. 创建实现类对象  
        Student s = new Student();  
        // 4. 调用方法  
        s.swimming();  
    }  
}

我们的目的,最终只是为了调用方法,那么能不能简化一下,把以上四步合成一步呢?匿名内部类就是做这样的快捷方式。

3.9.3 匿名内部类前提和格式

匿名内部类必须继承一个父类或者实现一个父接口

匿名内部类格式

new 父类名或者接口名(){  
    // 方法重写  
    @Override   
    public void method() {  
        // 执行语句  
    }  
};

3.9.4 使用方式

以接口为例,匿名内部类的使用,代码如下:

interface Swim {  
    public abstract void swimming();  
}  

public class Demo07 {  
    public static void main(String[] args) {  
        // 使用匿名内部类  
        new Swim() {  
            @Override  
            public void swimming() {  
                System.out.println("自由泳...");  
            }  
        }.swimming();  

        // 接口 变量 = new 实现类(); // 多态,走子类的重写方法  
        Swim s2 = new Swim() {  
            @Override  
            public void swimming() {  
                System.out.println("蛙泳...");  
            }  
        };  

        s2.swimming();  
        s2.swimming();  
    }  
}

3.9.5 匿名内部类的特点

  1. 定义一个没有名字的内部类
  2. 这个类实现了父类,或者父类接口
  3. 匿名内部类会创建这个没有名字的类的对象

3.9.6 匿名内部类的使用场景

通常在方法的形式参数是接口或者抽象类时,也可以将匿名内部类作为参数传递。代码如下:

interface Swim {  
    public abstract void swimming();  
}  

public class Demo07 {  
    public static void main(String[] args) {  
        // 普通方式传入对象  
        // 创建实现类对象  
        Student s = new Student();  
          
        goSwimming(s);  
        // 匿名内部类使用场景:作为方法参数传递  
        Swim s3 = new Swim() {  
            @Override  
            public void swimming() {  
                System.out.println("蝶泳...");  
            }  
        };  
        // 传入匿名内部类  
        goSwimming(s3);  

        // 完美关于方案: 一步到位  
        goSwimming(new Swim() {  
            public void swimming() {  
                System.out.println("大学生, 蛙泳...");  
            }  
        });  

        goSwimming(new Swim() {  
            public void swimming() {  
                System.out.println("小学生, 自由泳...");  
            }  
        });  
    }  

    // 定义一个方法,模拟请一些人去游泳  
    public static void goSwimming(Swim s) {  
        s.swimming();  
    }  
}

拼图游戏

主界面的制作

菜单的制作

图片的放置与打乱

事件

美化图片

移动图片

API

Math

Math.ceil:向上取整(进一法) Math.floor:向下取整(去尾法) Math.round:四舍五入 Math.pow(a,b)获取a的b次幂.如果第二个参数是0~1之间,则为你懂的 Math.sqrt:平方根

BigInteger

BigInteger.valueOf(16)

字符串中必须是整数,不然会被报错 字符串中的数字必须要跟进制吻合,比如二进制只能写01

对象一旦创建,内部记录的值不能发生改变

常见操作

底层储存方式

将整数转换为补码放在数组里.

BigDecimal

通过传递double类型的小数可能不精确, 可通过传入字符串表示的小数创建对象 或者使用valueOf静态方法

使用

System

Runtime

Object

  1. 浅克隆浅拷贝:共用一个地址值
  2. 深克隆深拷贝:数值类型的数据照常拷贝,引用类型的数据会开辟一个新的地址

Objects

对象工具类<提供一些操作对象的方法

正则表达式(Regex)

java中 \ 代表转义字符

(?i)忽略后面字符的大小写

一些例子:

  1. 验证手机号 String regex1 = "1[3-9]\\d{9}"
  2. 验证座机号 区号0\\d{2,3} - -? 号码[1-9]\\d{4,9} :号码的第一位不能是0开头,第二位开始可以是任意的数字号码总长度为5-10位
  3. 验证邮箱号码 String regex1 = "\\w+@[\\w$$[^_]]{2,6}(\\.[a-zA-Z]{2,3}){1,2}"

爬虫

  1. 贪婪爬取与有规则爬取

  2. 分组:就是小括号 捕获分组与非捕获分组

Date

SimpleDateFormat

  • 格式化

常用模式如下

  • 解析 : 把字符串表示的事件变为Date对象

public Data parse(String source) //解析(字符串日期对象)

Calendar

  • Calendar代表系统当前时间的日历对象,可以单独修改,获取事件中的年月日
  • 细节:Calendar是一个抽象类,不能直接创建对象

JDK8新增事件相关类

包装类

Integer

综合练习

常见算法

查找算法

排序算法

Arrays

操作数组的工具类

关于二分查找:如果找不到目标值,返回的是-插入点-1

关于拷贝数组:新数组小于老数组,部分拷贝,大于,会补上默认初始值

关于拷贝数组(指定范围):包头不包尾,包左不包右

关于排序:默认升序,底层快排 重写规则:

Lambda表达式

简化函数式接口的匿名内部类的写法

必须是接口的匿名内部类,接口中只能有一个抽象方法

面向对象 : 先找对象,后让对象做事情 函数式编程 : 强调做什么,而不是谁去做(JDK8后出现)

省略写法

可推导,可省略

  • 参数类型可以省略不写
  • 如果只有一个参数,()也可以不写
  • 如果Lambda表达式的方法体只有一行,大括号分号,ruturn可以省略不写,需要同时省略\

综合练习

集合进阶

类似c++ STL

List系列集合 : 添加的元素是有序,可重复,有索引

Set系列集合 : 全部相反

Collection 通用

注意点 在contains方法中,如果需要判断student等自定义类型的对象时,需要重写equals方法

Alt+Insert 在自定义类型类快捷创建

通用的遍历方式

迭代器遍历

细节注意点

  1. 报错NoSuchElementException(指针指出范围)
  2. 迭代器遍历完成后,指针不会复位
  3. 循环中只能用一次next方法
  4. 迭代器遍历时,不能用集合的方法进行增删改查,能用迭代器的方式删除it.remove()

增强for遍历

Lambda表达式遍历

精简模式 coll.forEach(s -> System.out.println(s))

List

就是增删改查

List集合的遍历方式

Collection的四种遍历方式 + 普通for循环(因为List集合存在索引)

遍历方式比较

删除元素:迭代器 添加元素:列表迭代器 仅仅想遍历:使用增强for或Lambda表达式 想操作索引:普通for

数据结构

前置知识补充,秒了

ArrayList

Linked List

  • 底层数据结构是双链表,查询慢,增删快

迭代器源码

泛型深入

  • 规范统一化添加的数据类型
  • 在编译期间九避免类型转换可能出现的异常
  • Java中为伪泛型,

需要将基本类型转换为包装类,intInteger

泛型类

泛型方法

可变参数

泛型接口

泛型的继承与通配符

  • 泛型不具有继承性,但数据具备继承性

Set

遍历方式

  • 迭代器
  • 增强for
  • Lambda表达式

同Collection

HashSet

JDK8以前,新元素存入数组,老元素挂后面 JDK8以后,新元素挂在老元素后面,当链表长度超过8且数组长度等于64时,自动转换为红黑树

重写方法

Alt+Insert 选择equals and hashCode

LinkedHashSet

继承于HashSetCollection,

特点:有序

底层基于哈希表,使用双链表添加数据顺序

TreeSet

特点:可排序

底层基于红黑树实现

  1. 默认规则:Javabean类实现Comparable接口指定比较规则

底层排序原理,跳过

  1. 比较器排序:创建TreeSet对象时,传递比较器Comparator指定规则

使用原则: 默认第一种,满足不了选择第二种

总结

Map(双列集合)

Map的遍历方式

  1. 键找值

  1. 键值对

  1. Lambda表达式

HashMap

public class MapDemo1 {  
    public static void main(String[] args) {  
        //需求:创建一个HashMap集合,键使学生对象(Student),值是籍贯(String)  
        //储存三个键值对元素,并遍历  
        //要求:同姓名,同年龄认为是一个学生  
  
        //HashMap的键位置如果存储的是自定义对象,需要重写hashcode和equls方法  
  
        //1.创建HashMap对象  
        HashMap<Student,String> hm = new HashMap<>();  
        //2.创建三个学生对象  
        Student s1 = new Student("zhangsan",23);  
        Student s2 = new Student("lisi",24);  
        Student s3 = new Student("wangwu",25);  
        Student s4 = new Student("wangwu",25);  
  
        //3.添加元素  
        hm.put(s1,"江苏");  
        hm.put(s2,"浙江");  
        hm.put(s3,"福建");  
        hm.put(s4,"沙东");  
  
        //4.遍历集合  
        Set<Student> keys = hm.keySet();  
        for (Student key : keys) {  
            String value = hm.get(key);  
            System.out.println(key + "=" + value);  
        }  
  
        System.out.println("--------------------------");  
        //ctrl+alt+v,hm.entrySet()  
        Set<Map.Entry<Student, String>> entries = hm.entrySet();  
        for (Map.Entry<Student, String> entry : entries) {  
            Student key = entry.getKey();  
            String value = entry.getValue();  
            System.out.println(key + "=" + value);  
        }  
  
        System.out.println("--------------------------");  
  
        hm.forEach(new BiConsumer<Student , String>() {  
            @Override  
            public void accept(Student student,String s){  
                System.out.println(student+":"+s);  
            }  
        });  
  
    }  
}

LinkHashMap

特点:

  1. 由键决定:有序
  2. 这里的有序是指存储和取出的元素一致

TreeMap

  • 与TreeSet底层原理一样,都是红黑树结构

特点:可对键排序 默认从小到大

三个案例

源码解析

可变参数

Collections

Strem流

单列集合

 //1.单列集合获取Stream流  
        ArrayList<String> list = new ArrayList<>();  
        Collections.addAll(list, "a", "b", "c", "d", "e", "f", "g", "h");  
//        //获取一条流水线,并将集合中的数据放到流水线上  
//        Stream<String> stream = list.stream();  
//        //使用终极方法打印,forEach快速重写alt+entrry  
//        stream.forEach(new Consumer<String>() {  
//            @Override  
//            public void accept(String s) {  
//                System.out.println(s);  
//            }  
//        });  
        list.stream().forEach(s -> System.out.println(s));

双列集合

//双列集合,无法直接使用stream流,需要调用keySet()间接调用  
  
HashMap<String,Integer> hm = new HashMap<>();  
hm.put("one",1);  
hm.put("two",2);  
hm.put("three",3);  
hm.put("four",4);  
  
hm.keySet().stream().forEach(s -> System.out.println(s));  
  
//第二种方法:entrySet()  
hm.entrySet().stream().forEach(s -> System.out.println(s));

数组与零散数据

//数组  
int[] arr = {1,2,3,4,5};  
//获取stream流  
Arrays.stream(arr).forEach(s -> System.out.println(s));  
//字符,引用等类型数组也可以  
//零散数据直接使用stream.of(内容)

Sream流中的中间方法

零碎知识:String方法.staetsWith() 原来的stream只能使用一次,推荐使用链式编程 修改流的数据不会改变集合或数组的数据

Stream流的终结方法

==快捷键:生成左边:ctrl+alt+v== #快捷键

long count = list.stream().count();  
System.out.println(count);

收集方法Collections

零碎知识:String方法.split()

收集的toMap里,较难,先跳过

方法引用

概述

:: 方法引用符

public class FunctionDemo1 {  
    public static void main(String[] args) {  
        Integer[] arr = {3,5,4,1,6,2};  
  
        //匿名内部类  
        Arrays.sort(arr,new Comparator<Integer>() {  
            @Override  
            public int compare(Integer o1, Integer o2) {  
                return o2-o1;//倒叙  
            }  
        });  
  
        //lambda表达式  
        //因为第二个参数的类型Comparator是一个函数式接口  
        Arrays.sort(arr,(Integer o1, Integer o2)->{  
            return o2-o1;//倒叙  
        });  
  
        //lambda表达式简化格式  
        Arrays.sort(arr,(o1,o2)->o2-o1);  
  
  
        //表示引用FunctionDemo1类里面的subtraction方法  
        //把这个方法当作抽象方法的方法体  
        Arrays.sort(arr,FunctionDemo1::subtraction);  
        System.out.println(Arrays.toString(arr));  
    }  
  
    //可以是java写好的,也可以是第三方写好的工具类  
    public static int subtraction(int num1, int num2) {  
        return num2-num1;  
    }  
  
}

方法引用的分类

引用静态方法

引用成员方法

引用构造方法

代码较多,先跳过

其他的调用方式

  1. 使用类名引用成员方法

此方法引用特殊

  1. 引用数组的构造方法

方法引用的练习

异常

  1. 编译时期异常

需手动处理,否则代码报错

  1. 运行时期异常

作用

  • 查看bug关键信息
  • 异常可作为方法内部的一种特殊的返回值,以便通知调用者的底层的执行情况(throw一个异常)

异常的处理方式

  1. JVM默认的处理方式

  2. 自己处理(捕获异常)

二问:只会报第一个错误,需要写多个catch捕获异常, 如果这些异常中存在父子关系时,父类一定要写在下面 三问:catch白写,最后交给虚拟机处理 四问:不会,直接跳catch

  1. 抛出异常

异常中的常见方法

快捷键 :选中可能出现的语句ctrl+alt+t 快速生成catch语句

练习

自定义异常

File

  • File对象就代表一个路径

File的常见成员方法

(判断,获取)

  • 获取文件大小
  • 返回文件路径
  • 获取文件名称与修改时间

(创建,删除)

注意点 delete方法默认只能删除文件和空文件夹,直接删除不走回收站

创建文件夹时,如果父级目录不存在,会抛出异常

(获取并遍历)

  • 有权限的文件时,返回null

综合练习

IO流

读写文件中的数据

分类

体系

FileoutputStream(字节输出流)

注意点 如果文件已经存在,则会覆盖文件 write方法的参数是整数,但实际上写到的是ascii上的对应的字符 每次使用完资源释放资源

换行与续写

第二个参数设为true

FileInputStream(字节输入流)

细节

  • 文件不存在会直接报错

循环读取

文件拷贝

基本代码

一次读多个字节

] 1024的整数倍 常用1024*1024*5

try..catch异常处理

快捷键

字符集

乱码原因

  • 读数据时未完整读完整个汉字
  • 读取规则不同 (用utf-8来统一)
  • 用字节流读取文件

Java中的编码解码方法

字符流

FileReader

FileWriter

底层原理

太长不看

高级流

字节缓冲流

字符缓冲流

提升不明显

转换流

(反)序列化流

(对象操作输(入)出流) 可以把Java中的对象写到本地文件中 **需要让对象implements Serializabel

如果成员变量不想被序列化,需加transient关键字修饰

打印流

字节打印流

字符打印流

同上

(解)压缩流

压缩文件夹

Commons-IO

使用步骤

Hutool

多线程

继承Thread类的方式进行实现

实现Runnable接口的方式进行实现

利用Callable接口和Future接口方式实现

(可实现接受多线程的结果)

常见的成员方法

线程安全问题

StringBuilderStirngBuffer (多线程更安全)

lock锁

注意死锁问题

等待唤醒机制思路分析(生产者与消费者)

等待唤醒机制思路分析(阻塞队列)

线程池

自定义线程池

  1. 临时线程只有在核心线程都在忙,且队列排满时才生效

线程池的选择

  1. CPU密集型运算:最大并行数+1
  2. I/O密集型运算:公式

多线程额外内容

网络编程

架构

三要素

IP

InetAddress

端口号

协议

UDP的通信程序

UDP的三种通信方式

好像就是把接收地址改为放在组播地址

TCP通信程序

反射

获取class对象

获取构造方法

获取成员变量

获取成员方法

动态代理(AOP?)

无侵入式的给代码增加额外的功能

通过接口保证,后面的对象和代理需要实现同一个接口,接口中就是被代理的所有方法