十、ArrayList集合
1.集合的基本使用
ps:集合只能存储引用数据类型的数据,如果需要存储基本数据类型则需要包装类
1.1. 基础成员方法
import java.util.ArrayList;
public class ArrayListDemo1 {
public static void main(String[] args) {
//1.创建集合的对象
//泛型:限定集合中存储的数据类型; 集合中只能存储引用数据类型
ArrayList<String> list = new ArrayList<>();
list.add("AAA");
list.add("BBB");
list.add("CCC");
list.add("DDD");
list.set(3, "EEE");
System.out.println(list.size());
list.remove(1);
System.out.println(list);
System.out.println(list.get(2));
}
}
1.2. 基本数据类型包装类
1.3. 自定义集合存类
package test;
import java.sql.ClientInfoStatus;
import java.util.ArrayList;
public class StudentTest {
public static void main(String[] args) {
Student s1 = new Student("小明", 10);
Student s2 = new Student("小李", 19);
Student s3 = new Student();
ArrayList<Student> students = new ArrayList<>();
students.add(s1);
students.add(s2);
students.add(s3);
System.out.println(students);
//[Student{name = 小明, age = 10}, Student{name = 小李, age = 19}, Student{name = null, age = 0}]
// 遍历
for (int i = 0; i < students.size(); i++) {
Student s = students.get(i);
System.out.println(s.getName() + " " + s.getAge());
}
}
}
2. 综合训练学生信息管理系统
public class Student {
private String id;
private String name;
private int age;
private String address;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
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 getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Student(String id, String name, int age, String address) {
this.id = id;
this.name = name;
this.age = age;
this.address = address;
}
public Student() {
}
}
import java.sql.SQLOutput;
import java.util.ArrayList;
import java.util.Scanner;
public class StudentSystem {
public static void main(String[] args) {
ArrayList<Student> students = new ArrayList<Student>();
loop:
while (true) {
System.out.println("----------欢迎来到KissFeng学生信息管理系统------------");
System.out.println("1.添加学生");
System.out.println("2.删除学生");
System.out.println("3.修改学生");
System.out.println("4.查询学生");
System.out.println("5.退出程序");
Scanner sc = new Scanner(System.in);
System.out.println("请输入您的选项:");
String choice = sc.next();
switch (choice) {
case "1" -> addStudent(students);
case "2" -> deleteStudent(students);
case "3" -> updateStudent(students);
case "4" -> queryStudent(students);
case "5" -> {
System.out.println("退出系统");
break loop;
// System.exit(0);
}
default -> System.out.println("输入错误");
}
}
}
//添加学生
public static void addStudent(ArrayList<Student> students) {
Student student = new Student();
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("请输入学生的ID:");
String id = sc.next();
if (checkID(students, id)) {
student.setId(id);
System.out.println("请输入学生姓名:");
String name = sc.next();
student.setName(name);
System.out.println("请输入学生年龄:");
int age = sc.nextInt();
student.setAge(age);
System.out.println("请输入学生住址:");
String address = sc.next();
student.setAddress(address);
students.add(student);
break;
} else System.out.println("ID重复,请重新输入!");
}
System.out.println("----------添加完毕----------");
}
//删除学生
public static void deleteStudent(ArrayList<Student> students) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入您要删除的学生的学号:");
String id = sc.next();
if (!checkID(students, id)) {
for (int i = 0; i < students.size(); i++) {
if (students.get(i).getId().equals(id)) {
students.remove(i);
System.out.println("----------删除成功----------");
}
}
} else System.out.println("该学号不存在,请重新操作!");
}
//修改学生
public static void updateStudent(ArrayList<Student> students) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入您要修改的学生的学号:");
String id = sc.next();
if (!checkID(students, id)) {
for (int i = 0; i < students.size(); i++) {
if (students.get(i).getId().equals(id)) {
System.out.println("请输入新姓名:");
String newName = sc.next();
System.out.println("请输入新年龄:");
int newAge = sc.nextInt();
System.out.println("请输入新地址:");
String newAddress = sc.next();
students.get(i).setName(newName);
students.get(i).setAge(newAge);
students.get(i).setAddress(newAddress);
System.out.println("----------修改成功----------");
}
}
} else System.out.println("该学号不存在,请重新操作!");
}
//查询学生
public static void queryStudent(ArrayList<Student> students) {
if (students.size() != 0) {
System.out.println("学号\t" + "姓名\t" + "年龄\t" + "地址");
for (int i = 0; i < students.size(); i++) {
System.out.println(students.get(i).getId() + "\t\t" + students.get(i).getName() + "\t" + students.get(i).getAge() + "\t\t" + students.get(i).getAddress());
}
} else System.out.println("当前无学生信息,请添加后操作");
System.out.println("----------查询完毕----------");
}
// 学生ID存在性检验
public static Boolean checkID(ArrayList<Student> students, String id) {
for (int i = 0; i < students.size(); i++) {
if (students.get(i).getId().equals(id)) return false;
}
return true;
}
}
十一、面向对象Pro
1. static静态变量
1.1. static修饰的特点
静态方法只能访问静态变量和静态方法
非静态方法可以访问静态变量或者静态方法,也可以访问非静态的成员变量和非静态的成员方法
静态方法中是没有this关键字
1.2. 类的区分
JavaBean类:用来描述一类事物的类。比如,Student,Teacher,Dog,Cat等。
测试类:用来检查其他类是否书写正确带有main方法的类,是程序的入口。
工具类:不是用来描述一类事物的,而是帮我们做一些事情的类。
2. 继承
2.1. 基础知识
概念:当类与类之间,存在相同(共性)的内容,并满足子类是父类中的一种,就可以考虑使用继承,来优化代码。Java中提供一个关键字extends,用这个关键字,我们可以让一个类和另一个类建立起继承关系。
好处:可以把多个子类中重复的代码抽取到父类中,子类可以直接使用,减少代码兄余,提高代码的复用性。
继承的格式:
使用继承的好处:
可以把多个子类中重复的代码抽取到父类中了,提高代码的复用性。
子类可以在父类的基础上,增加其他的功能,使子类更强大。
继承的特点:Java只支持的那继承,不支持多继承,但支持多层继承。
多层继承案例:
package demo1;
public class Test {
public static void main(String[] args) {
// 布偶猫
Ragdoll d1 = new Ragdoll();
d1.eat();
d1.drink();
d1.catchMouse();
System.out.println("-------------------------");
// 哈士奇
Husky h1 = new Husky();
h1.eat();
h1.drink();
h1.lookHome();
h1.breakHome();
}
}
// 吃东西
// 喝水
// 猫抓老鼠
// -------------------------
// 吃东西
// 喝水
// 狗看家
// 拆家
2.2. 子类能继承父类哪些东西
成员方法的继承规则:
package demo3;
public class Test {
public static void main(String[] args) {
Zi z1 = new Zi();
z1.ziShow();
}
}
class Fu{
String name = "Fu";
}
class Zi extends Fu{
String name = "Zi";
public void ziShow(){
String name = "ZiShow";
System.out.println(name);
System.out.println(this.name);
System.out.println(super.name);
}
}
// ZiShow
// Zi
// Fu
2.3. 方法的重写
方法重写的要求:
package demo4;
public class Test {
public static void main(String[] args) {
OverseaStudent s1 = new OverseaStudent();
s1.lunch();
}
}
class Person{
public void eat() {
System.out.println("吃米饭,吃菜");
}
public void drink() {
System.out.println("喝开水");
}
}
class OverseaStudent extends Person{
public void lunch() {
this.eat();//吃意大利面
this.drink();//喝凉水
super.eat();//吃米饭,吃菜
super.drink();//喝开水
}
@Override
public void eat() {
System.out.println("吃意大利面");
}
@Override
public void drink() {
System.out.println("喝凉水");
}
}
2.4. 继承中的构造方法
子类不会继承父类的构造方法
子类会默认访问父类的无参构造方法
若想要调用父类含参构造方法,则需要手动写子类的含参构造并super调用父类的构造方法
3. 多态
3.1. 认识多态
3.2. 多态中调用成员的特点
调用成员变量的特点:编译看左边,运行也看左边
调用成员方法的特点:编译看左边,运行看右边
package com.itheima.a02polymorphismdemo2;
public class Test {
public static void main(String[] args) {
//创建对象(多态方式)
//Fu f = new Zi();
Animal a = new Dog();
//调用成员变量:编译看左边,运行也看左边
//编译看左边:javac编译代码的时候,会看左边的父类中有没有这个变量,如果有,编译成功,如果没有编译失败。
//运行也看左边:java运行代码的时候,实际获取的就是左边父类中成员变量的值
System.out.println(a.name);//动物
//调用成员方法:编译看左边,运行看右边
//编译看左边:javac编译代码的时候,会看左边的父类中有没有这个方法,如果有,编译成功,如果没有编译失败。
//运行看右边:java运行代码的时候,实际上运行的是子类中的方法。
a.show();///Dog --- show方法
//理解:
//Animal a = new Dog();
//现在用a去调用变量和方法的呀?是的
//而a是Animal类型的,所以默认都会从Animal这个类中去找
//成员变量:在子类的对象中,会把父类的成员变量也继承下的。父:name 子:name
//成员方法:如果子类对方法进行了重写,那么在虚方法表中是会把父类的方法进行覆盖的。
}
}
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方法");
}
}
class Cat extends Animal{
String name = "猫";
@Override
public void show() {
System.out.println("Cat --- show方法");
}
3.3. 多态的优势和弊端
public class Test {
public static void main(String[] args) {
//创建对象
Animal a = new Dog();
//编译看左边,运行看右边
a.eat();
//多态的弊端
//不能调用子类的特有功能
//报错的原因?
//当调用成员方法的时候,编译看左边,运行看右边
//那么在编译的时候会先检查左边的父类中有没有这个方法,如果没有直接报错。
//a.lookHome();
//解决方案:
//变回子类类型就可以了
//细节:转换的时候不能瞎转,如果转成其他类的类型,就会报错
//Cat c = (Cat) a;
//c.catchMouse();
/*if(a instanceof Dog){
Dog d = (Dog) a;
d.lookHome();
}else if(a instanceof Cat){
Cat c = (Cat) a;
c.catchMouse();
}else{
System.out.println("没有这个类型,无法转换");
}*/
//新特性
//先判断a是否为Dog类型,如果是,则强转成Dog类型,转换之后变量名为d
//如果不是,则不强转,结果直接是false
if(a instanceof Dog d){
d.lookHome();
}else if(a instanceof Cat c){
c.catchMouse();
}else{
System.out.println("没有这个类型,无法转换");
}
}
}
class Animal{
public void eat(){
System.out.println("动物在吃东西");
}
}
class Dog extends Animal{
@Override
public void eat() {
System.out.println("狗吃骨头");
}
public void lookHome(){
System.out.println("狗看家");
}
}
class Cat extends Animal{
@Override
public void eat() {
System.out.println("猫吃小鱼干");
}
public void catchMouse(){
System.out.println("猫抓老鼠");
}
}
4. Java的包
导包使用其他包中的类的规则:
package com.itheima.test;
//import com.itheima.domain1.Teacher;
public class Test {
public static void main(String[] args) {
//同一个包中的类不需要导包
/*Student s1 = new Student();
s1.setName("张三");
s1.setAge(23);
System.out.println(s1.getName()+" "+s1.getAge());*/
//java.lang包中的类无需导包
/*String s1 = "abc";
System.out.println(s1);*/
//使用其他包中的类
/*Teacher t = new Teacher();*/
//调用两个包中的同名类,需要用类全名,不导包了
com.itheima.domain1.Teacher t1 = new com.itheima.domain1.Teacher();
com.itheima.domain2.Teacher t2 = new com.itheima.domain2.Teacher();
}
}
5. final关键字
final修饰方法:表明该方法是最终方法,不能被重写
final修饰类:表明该类是最终类,不能被继承
final修饰变量:叫做常量,只能被赋值一次
不能再进行改变
package test2;
public class Test {
public static void main(String[] args) {
/*
final 修饰基本数据类型,记录的值不能发生改变
final 修饰引用数据类型,记录的地址值不能发生改变,但内部属性还是可以变化
*/
final double PI = 3.14;
// PI = 3.1415926; 会报错
final int[] arr = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
arr[1] = 100;
System.out.println(arr[1]);
// 这样可以修改成功
}
}
6. 权限修饰符
权限修饰符:是用来控制一个成员能够被访问的范围的,
可以修饰成员变量,方法,构造方法,内部类。
demo1:
package demo1;
public class Animal {
private String name;
public void show(){
System.out.println(name);
}
}
package demo1;
public class Test {
public static void main(String[] args) {
Animal a = new Animal();
// System.out.println(a.name); 会报错,因为a.name的属性为私有属性
a.show(); //能够执行,因为show方法为public方法
}
}
demo2:
package demo2;
import demo1.Animal;
public class Dog extends Animal{
@Override
public void show() {
System.out.println(name);
}
}
package demo2;
import javax.xml.namespace.QName;
public class Test {
public static void main(String[] args) {
Dog d = new Dog();
d.setName("abb");
System.out.println(d.getName());
}
}
实际开发中,一般只使用private和public:用于成员变量私有或者方法公开
7. 代码块
7.1. 局部代码块(当前不常用)
在标红的 { } 中变量a提前结束,立马回收变量a,节省内存
7.2. 构造代码块
将对象的构造方法中重复的代码块提取出来放到构造代码块中
左边与右边等同,将左边以构造代码块的形式写到右面,优先于构造方法执行
构造代码块:
1、位置:写在成员位置的代码块
2、作用:可以把多个构造方法中重复的代码抽取出来
3、执行时机:我们在创建本类对象的时候会先执行构造代码块再执行构造方法
package demo1;
public 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;
}
如果有些代码不想用构造代码块,解决方法:
1是在空参构造方法中调用含参构造 2是将代码块做成一个方法进行调用
7.3. 静态代码块
想做一些数据初始化且只执行一次,使用这种方法。
8. 抽象类和抽象方法
8.1. 概念
8.2. 定义格式
8.3. 注意事项
1、抽象类不能实例化。
2、抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类。
3、可以有构造方法。
4、抽象类的子类应当重写抽象类中的所有抽象方法,或者也是抽象类。
package demo1;
public abstract class Person {
// 一旦方法变为抽象,则类也会变为抽象 报错使用alt+键
public abstract void work();
}
9. 接口
9.1. 基础
接口就是一种规则,是对行为的抽象
接口的定义和使用:
注意:
9.2. 细节
//定义的内容只能是常量,默认使用public static final修饰
//没有构造方法
//成员方法只能是抽象方法,默认修饰符public abstract
//多个接口有重复的方法,则只需要重写一次这个重复的方法
内存分析:黑马程序员Java零基础视频P137四分钟
获取内存:Termainal中输入jps
进入内存分析工具:jhsdb hsdb
9.3. 接口和类之间的关系
类和类的关系:
继承关系,只能单继承,不能多继承,但是可以多层继承
类和接口的关系:
实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口
接口和接口的关系:
继承关系,可以单继承,也可以多继承。
如果实现类实现了最下面的子接口,那么就需要重写所有的抽象方法
9.4. 接口默认方法
接口中默认方法的定义格式:
public default 返回值类型 方法名(参数列表){ }
接口中默认方法的注意事项:
1.默认方法不是抽象方法,所以不强制被重写。但是如果被重写,重写的时候去掉default关键字
2.public可以省略,default不能省略
3.如果实现了多个接口,多个接口中存在相同名字的默认方法,子类就必须对该方法进行重写
9.5. 接口中静态方法
接口中静态方法的定义格式:
格式:public static 返回值类型 方法名(参数列表){ }
范例:public static void show(){ }
接口中静态方法的注意事项:
静态方法只能通过接口名调用,不能通过实现类名或者对象名调用public可以省略,static不能省略
9.6. 适配器设计模式
设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。
简单理解:设计没说就是各种套路。
适配器设计模式:解决接口与接口实现类之间的矛盾问题。
适配器就是Java文件和Interface文件中用一个抽象类空实现端口中的所有抽象方法,然后实际开发的Java文件继承这个适配器文件抽象类
1.当一个接口中抽象方法过多,但是我只要使用其中一部分的时候,就可以适配器设计模式
2.书写步骤
编写中间类XXXAdapter,实现对应的接口对接口中的抽象方法进行空实现让真正的实现类继承中间类,并重写需要用的方法为了避免其他类创建适配器类的对象,中间的适配器类用abstract进行修饰
10. 内部类
类的五大成员:方法、属性、构造方法、代码块、内部类
①内部类表示的事物是外部类的一部分
②内部类单独出现没有任何意义
③内部类的访问特点:
内部类可以直接访问外部类的成员,包括私有
外部类要访问内部类的成员,必须创建对象
10.1. 成员内部类
写在成员位置的,属于外部类的成员
成员内部类可以被一些修饰符所修饰,比如:private, 默认, protected, public, static等
在成员内部类里面,JDK16之前不能定义静态变量,JDK16开始才可以定义静态变量
package demo3;
public class Test {
public static void main(String[] args) {
Outer o = new Outer();
Outer.Inner i = o.new Inner();
i.show();
}
}
class Outer{
private int a = 10;
class Inner{
private int a = 20;
public void show(){
int a = 30;
System.out.println(Outer.this.a); //10 ***重点
System.out.println(this.a); //20
System.out.println(a); //30
}
}
}
10.2. 静态内部类
静态内部类只能访问外部类中的静态变量和静态方法
如果想要访问非静态的需要创建对象
创建静态内部类对象的格式:外部类名.内部类名 对象名 = new 外部类名.内部类名( );
调用非静态方法的格式:先创建对象,用对象调用
调用静态方法的格式:外部类名.内部类名.方法名( );
10.3. 局部内部类
①将内部类定义在方法里面就叫做局部内部类,类似于方法里面的局部变量。
②外界是无法直接使用,需要在方法内部创建对象并使用。
③该类可以直接访问外部类的成员,也可以访问方法内的局部变量。
10.4. 匿名内部类(重要)
匿名内部类本质上就是隐藏了名字的内部类。
这个整体就相当于是这个匿名类重写方法之后的对象。
package demo5;
public abstract class Animal {
public abstract void eat();
}
package demo5;
public interface Swim {
public void swim();
}
package demo5;
public class Test {
public static void main(String[] args) {
//编写匿名类
new Swim(){
//重写游泳方法
@Override
public void swim() {
System.out.println();
}
};
//method中的内容就是匿名类对象
method(
new Animal(){
@Override
public void eat() {
System.out.println("吃");
}
}
);
}
public static void method(Animal a){
//编译看左边,运行看右边
a.eat();
}
}
package demo5;
public class Test2 {
static Animal a = new Animal() {
@Override
public void eat() {
System.out.println("成员位置eat");
}
};
public static void main(String[] args) {
Animal a = new Animal() {
@Override
public void eat() {
System.out.println("主函数内部eat");
}
};
a.eat();
}
}