记录自己的Java学习过程
从类的继承开始
目录
一、继承
1.修饰词与继承的关系
*图片来自哔站比特博哥
现在inherit包中创建一个Animal类
package inherit;
public class Animal {
public String name;
public int age;
String def; //默认为default类型
protected String pro;
}
1.public
在不同包不同类之间都可以直接引用
//public在不同包不同类都可以使用(任何地方)
Animal animal = new Animal();
animal.name = "public";
animal.age = 3;
animal.eat();
2.protected
在同一个包中的不同类可以使用
也可以在不同包中的子类中使用(不同包的非子类不可以引用)
在同一个包中可以随便调用
package inherit;
public class Test2 {
public static void main(String[] args) {
//protect在同一个包中的不同类也可以使用
Animal animal1 = new Animal();
animal1.pro = "Protected";
}
}
在不同包中只能通过继承才能调用
package combination;
import inherit.Animal;
//也可以在不同包中的子类使用
public class Test1 extends Animal{
public void main(String[] args) {
this.pro = "aaa";
}
3.default
在同一个包中的不同类引用(不同包之间不可以引用)
package inherit;
public class Test2 {
public static void main(String[] args) {
//default在同一个包中的不同类可以使用
Animal animal2 = new Animal();
animal1.def = "default";
}
}
2. 继承与构造函数
父类存在有参或无参构造时,子类必须也进行有参或无参构造,子类在构造时,可以用super对父类进行构造,再对自己独有的成员进行构造。
public class Animal {
public String name;
public int age;
//父类存在有参构造,想使用无参构造时必须进行无参构造
public Animal() {
}
//父类有参构造
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
}
class Dog extends Animal {
public boolean silly;
//父类存在有参构造,想使用无参构造时必须进行无参构造
public Dog() {
super();
}
//如果父类存在带参构造,子类必须进行有参构造
public Dog(String name, int age, boolean silly) {
super(name, age);//先对父类进行构造
this.silly = silly;//在对子类进行构造
}
}
3.继承与静态区函数
静态区的方法在实例化时自动执行,先执行父类,再执行子类,且只能执行一次
public class Animal {
public String name;
public int age;
//父类静态区
static{
System.out.println("Animal::static");
}
}
class Dog extends Animal {
public boolean silly;
//子类静态区
static {
System.out.println("Dog::static");
}
}
public class Test2 {
public static void main(String[] args) {
Dog dog = new Dog();//先调用Dog,会先执行父类的static区的方法,再执行子类static区的方法
Dog dog1 = new Dog();//但静态区的都只会执行一次,在第二次调用的时候就不执行了
}
}
运行结果:
Animal::static
Dog::static
4.super的用法
super会达到易读的效果
this会访问子类以及父类的变量(从子类到父类寻找)
super会直接从父类中寻找super用法
1. super.data 访问父类的普通成员变量
2. super.func() 调用父类的普通成员方法
3. super() 调用父类的构造方法注意:只能调用非静态成员,静态成员必须用类名访问
class Base {
int a;
public void methodBase() {
System.out.println("methodBase");
}
}
class Child extends Base {
int a;
public void func1() {
a = 10;//在子类和父类有同名的变量时会先访问子类的
super.a = 100;//利用super直接访问父类的成员
System.out.println(a);//10
System.out.println(super.a);//super可以直接访问父类的变量 100
}
public void methodChild() {
System.out.println("methodChild");
}
public void methodBase() {
System.out.println("Child_methodBase");
}
public void func2() {
methodChild();//现在子类中找,找到就引用
methodBase();//子类中存在找,直接引用
super.methodBase();//利用super来直接访问父类的对象
}
}
5.组合
组合就是同一个类面实例化其他类,相比于继承更加常用
与C语言的结构体类似
class Student {
}
class Teacher {
}
//以后使用组合的情况比较多
class School {
public Student[] students;
public Teacher[] teachers;
}
二、多态
重写
快捷键 Alt + Insert -> ctrl + o
1.private修饰的方法不能重写
2.static修饰的方法不能重写
3.final(密封)修饰的方法不能重写
4.返回值可以不同,但必须有父子关系
5. 重写的参数列表相同
创建一个Animal父类 ,有一个eat的成员函数
class Animal {
public String name;
public int age;
public void eat() {
System.out.println(name + "吃饭");
}
}
再定义两个子类,Dog类和Cat类
class Dog extends Animal {
//Dog类独有的狗叫
public void bark() {
System.out.println(name + "在叫");
}
//有特殊需求需要重写
//原理 子类访问的优先权大于父类
//对eat进行重写
public void eat() {
System.out.println(name + "吃骨头");
}
}
class Cat extends Animal {
//Cat类独有的抓老鼠
public void catchMouse() {
System.out.println(name + "在抓老鼠");
}
//有特殊需求需要重写
//对eat进行重写
public void eat() {
System.out.println(name + "在吃鱼");
}
}
发生多态
public class Main {
public static void function(Animal animal) {
//接收父类对象,可以是子类类型
//方法传参时可以进行向上转型
animal.eat();
//判断传入进来的animal具体是Dog还是Cat,然后强转后可以调用他们独有的方法
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
dog.bark();
} else {
Cat cat = (Cat) animal;
cat.catchMouse();
}
}
public static void main(String[] args) {
//向下转型
Animal dog = new Dog();
dog.name = "狗";
Animal cat = new Cat();
cat.name = "咪咪";
function(dog);//传入父类类型的子类对象
function(cat);
}
}
三、抽象类
1. 抽象类,使用abstract修饰类
2. 抽象类当中,可以包含普通类所能包含的成员
3. 抽象类和普通类不一样的是,抽象类当中可以包含抽象方法
4. 抽象方法是使用abstract修饰的,这个方法,没有具体实现
5. 不能实例化抽象类
6. 抽象类存在的最大意义就是被继承
7. 如果一个普通类继承了一个抽象类,必须重写所有抽象类的方法
8. 如果一个抽象类A继承了一个抽象类B,此时A当中 不需要重写B中的抽象方法,但如果A再被普通类继承,就必须重写A和B的所有抽象类
9. 抽象方法不能是私有的,static? 也就是要满足重写的规则
10.final不可以,他和abstract是矛盾的
11.抽象类当中 可以有构造的方法,为了方便子类能够调用,来初始化抽象类的成员
1.创建抽象类 (使用abstract修饰整个类,该抽象类的内部成员不能被实例化)
abstract class Shape {
//抽象方法,不用实现
public int a;
//抽象函数也可有public的构造方法,但不能有abstract修饰
public Shape(int a) {
this.a = a;
}
public abstract void draw();
public abstract void eat();
//抽象类的普通方法可以实现,并可以被继承,不必要进行重写,必要时也可以重写
public void func() {
System.out.println("func()");
}
}
2.创建第二个抽象类,继承第一个抽象类(抽象类之间可以有继承关系)
abstract class Shape2 extends Shape {
public int b;
//对父类以及自己进行构造
public Shape2(int a, int b) {
super(a);
this.b = b;
}
public abstract void brak();
}
3.创建一个普通类来继承抽象类(继承抽象子类)
//如果继承了抽象子类,那么孙子必须重写父类和子类的所有抽象方法
class Rect2 extends Shape2 {
//对父类都进行构造
public Rect2(int a,int b) {
super(a,b);
}
//对父亲的重写
public void brak() {
System.out.println("Rect2::brak()");
}
//对爷爷的重写
public void draw() {
System.out.println("Rect2::draw()");
}
//对爷爷的重写
public void eat() {
System.out.println("Rect2::eat()");
}
}
进行实例化
public class Main {
public static void main(String[] args) {
Rect2 rect2 = new Rect2(10, 20);
rect2.func();
rect2.brak();
rect2.eat();
rect2.draw();
}
}
运行结果如下
func()
Rect2::brak()
Rect2::eat()
Rect2::draw()
四、接口
1.使用interface来修饰接口 2.接口当中的成员方法,不能有具体的实现
a.抽象方法:默认是public abstract的方法
b.允许可以使用可实现的方法,但这种只能是由default修饰的
c.可以实现有一个静态方法(只能通过接口名称进行调用)
3.成员变量默认是由public static final 修饰的
4.接口不能被实例化
5.类和接口是利用implements来链接,可以一个类对应多个接口,用逗号来隔开
6.接口名称一般以I开头,接一个形容词
7.子类重写抽象方法时必须是public修饰
8.接口中不能有静态代码块和构造方法
9.如果类没有实现接口中所有的抽象方法,则类必须设置为抽象类
10.接口可以解决Java中不能多继承的问题 java不能继承多个类,但可以继承多个接口
1.接口的一般使用方法
定义两个接口
//第一个接口
interface IShape {
int size = 10;
//即public static final int size = 10;
//抽象的方法
void draw();
//具体实现的方法必须有default修饰
default public void func() {
System.out.println("default方法");
}
静态方法可以实现(只能通过接口名称进行调用)
public static void func2() {
System.out.println("public static");
}
}
//第二个接口
interface IShape2 {
void draw2();
}
一个普通类可以调用多个接口(可以解决Java中不能多继承的问题)
class Person implements IShape,IShape2 {
//默认的方法必须重写,即抽象方法必须重写
//且必须重写所有接口的抽象方法
public void draw() {
System.out.println("Person 的重写");
}
public void draw2() {
System.out.println("重写draw2()");
}
在实例化时可以做到多个普通类同时调用同一个接口,此时会发生多态,可以使用与继承相同的方法,如下
//新定义一个普通类来调用 第一个接口
class Student implements IShape {
public void draw() {
System.out.println("Student 的重写");
}
}
public class Main {
//接口也可以向下转型
public static void drawMap(IShape iShape) {
//使用此方法也可以单独的调用子类独有的方法
if (iShape instanceof Person) {
Person person = (Person) iShape; //和继承中的强转是一个道理
person.draw();
} else {
Student student = (Student) iShape;
student.draw();
}
}
public static void main(String[] args) {
//定义一个Person类
Person person = new Person();
//这里也可以使用
//IShape person = new Person();
drawMap(person); //输出Person 的重写
//定义一个Student类
Student student = new Student();
//这里也可以使用
//IShape student = new Student();
drawMap(student); //输出Student 的重写
IShape.func2(); //通过接口名来调用接口的静态方法
}
}
2.接口与类的继承同时使用
接口可以解决Java中不能多继承的问题(即在继承父类的同时可以调用其他多个接口)
java不能继承多个类,但可以继承多个接口
定义两个接口和一个父类
//游泳的接口
interface ISwimming {
void swimming();
}
//飞的接口
interface IFly {
void fly();
}
//抽象Animal类
abstract class Animal {
public String name;
public Animal() {
}
public Animal(String name) {
this.name = name;
}
public abstract void eat();
}
再定义两个子类Dog和Bird都继承父类Animal,并让他们分别调用ISwimming接口和Ifly接口
这样可以分别使Dog类调用ISwimming接口,Bird类调用IFly接口
class Dog extends Animal implements ISwimming {
public Dog() {
}
public Dog(String name) {
this.name = name;
}
//重写父类
public void eat() {
System.out.println(name + "在吃狗粮");
}
//重写接口
public void swimming() {
System.out.println(name + "在游泳");
}
}
class Dog extends Animal implements ISwimming {
public Dog() {
}
public Dog(String name) {
this.name = name;
}
//重写父类
public void eat() {
System.out.println(name + "在吃狗粮");
}
//重写接口
public void swimming() {
System.out.println(name + "在游泳");
}
}
class Bird extends Animal implements IFly {
public Bird() {
}
public Bird(String name) {
this.name = name;
}
//重写父类
public void eat() {
System.out.println(name + "在吃虫子");
}
//重写接口
public void fly() {
System.out.println(name + "在飞");
}
}
进行实例化
public class Test {
public static void main(String[] args) {
Dog dog = new Dog("狗");
dog.eat();
dog.swimming();
Bird bird = new Bird("鸟");
bird.eat();
bird.fly();
}
}
运行结果如下
狗在吃狗粮
狗在游泳
鸟在吃虫子
鸟在飞
3.接口之间的调用(继承)
接口之间是可以调用的,普通类在调用辈分最小的接口时需要重写族内所有的方法
interface A {
void func1();
}
interface B {
void func2();
}
//接口之间可以继承,甚至多继承
interface C extends A, B {
void func3();
}
//若调用一个子类接口,则必须将所有父类以及该接口的方法都重写
class Perosn1 implements C{
@Override
public void func3() {
}
@Override
public void func1() {
}
@Override
public void func2() {
}
}
4.一些常用的内置接口实例
Cloneable 接口
import java.util.Comparator;
类可以通过调用Java内置的Cloneable接口来重写clone方法进行对类变量的拷贝
可以实现浅拷贝或深拷贝
class Money {
public int m;
public Money(int m) {
this.m = m;
}
}
//Cloneable 可克隆的
class Student3 implements Cloneable{
public String name;
public int age;
public Money money;
public Student3(String name, int age) {
this.name = name;
this.age = age;
money = new Money(10);
}
//相当于重写父类的克隆,再返回父类的克隆
//(因为是protected修饰,在不同包之间不可以直接引用,所以需要重写
//直接通过ctrl + Insert -> ctrl + o 进行添加
@Override
protected Object clone() throws CloneNotSupportedException {
//浅拷贝(直接返回父类的clone方法,进行浅拷贝)
//return super.clone();
//深拷贝(对于自己需要创建新空间的成员变量需要强制转换父类返回的clone值并对其开辟新空间
Student3 temp = (Student3) super.clone();
temp.money = new Money(temp.money.m);
return temp;
}
@Override
public String toString() {
return "Student3{" +
"name='" + name + '\'' +
", age=" + age +
", money=" + money.m +
'}';
}
}
实例化
public class Test3 {
public static void main(String[] args) throws CloneNotSupportedException {
Student3 student3 = new Student3("zhiasd", 10);
//因为clone返回的是object类型,所以需要强制转换一下
Student3 student31 = (Student3) student3.clone();
student31.money.m = 100000; //若是浅拷贝,则两个students对象的money指向的m是同一个
//深拷贝之后就会指向不同空间
System.out.println(student3);
System.out.println(student31);
}
}
运行结果
[Student3{name='aasd', age=5, money=10},
Student3{name='basd', age=10, money=10},
Student3{name='bfsd', age=7, money=10}]
Comparator 接口
Comparator 接口 用类创建比较器,对类数组进行排序
import java.util.Comparator;
创建两个类,都调用Comparator接口
//比较器 (比较年龄大小)按年龄排序 //Comparator对类的侵入性比较弱
class AgeComparator implements Comparator<Student3> {
@Override
public int compare(Student3 o1, Student3 o2) {
return o1.age - o2.age;
}
}
//比较器(比较名字大小)按姓名排序
class NameComparator implements Comparator<Student3> {
@Override
public int compare(Student3 o1, Student3 o2) {
return o1.name.compareTo(o2.name);//compareTo是String中自带的类,返回一个整数
}
}
利用连个比较类进行排序
public class Test3 {
public static void main(String[] args) throws CloneNotSupportedException {
Student3[] student3s = new Student3[3];
student3s[0] = new Student3("basd",10);
student3s[1] = new Student3("aasd", 5);
student3s[2] = new Student3("bfsd", 7);
AgeComparator ageComparator = new AgeComparator();//以年龄进行排序
NameComparator nameComparator = new NameComparator();//以姓名排序
//必须传入实例化之后的比较类变量
//按年龄排序
Arrays.sort(student3s, ageComparator);
System.out.println(Arrays.toString(student3s));
//按姓名排序
Arrays.sort(student3s, nameComparator);
System.out.println(Arrays.toString(student3s));
}
}
运行结果
[Student3{name='aasd', age=5, money=10},
Student3{name='bfsd', age=7, money=10},
Student3{name='basd', age=10, money=10}]
[Student3{name='aasd', age=5, money=10},Student3{name='basd', age=10, money=10},
Student3{name='bfsd', age=7, money=10}]
五、Object类
Object类即所有类的父类
class Person {
public void func1() {
}
}
class Student {
public void func2() {
}
}
public class Main {
//默认接收所有类的父类
public static void Test(Object object) {
if (object instanceof Person) {
Person person = (Person) object;
person.func1();
} else {
Student student = (Student) object;
student.func2();
}
}
public static void main(String[] args) {
Test(new Person());//可以传入匿名对象
Test(new Student());
}
}