目录
选中src右键选择new-->package,创建名字

创建好名字后在下方新建一个testwww

建完的界面就是这样滴


在自定义包下创建新程序TestOne和一个新的包demo,在demo下再创建一个新程序TestTwo

- //TestOne
- package com.bitejiuyeke.www;
-
- public class TestOne {
- String name = "zhangsan";
-
- public static void main(String[] args) {
-
- }
- }

因为封装在TestTwo中无法引用TestOne中的变量。
假设学生都在一个教室上课
我们就把classroom拿出来当成静态类

public static String classroom;
特性:
- public class Student{
- // ...
- private static String classRoom = "Bit306";
- // ...
- public static String getClassRoom(){
- return classRoom;
- }
- }
- public class TestStudent {
- public static void main(String[] args) {
- System.out.println(Student.getClassRoom());
- }
- }
特性
初始化
1.就地初始化
直接赋值
2.代码块初始化


结论:
静态代码块先执行
然后执行构造代码块
在实行对应的构造方法
静态的只执行1次
同步代码块
这些静态代码块可以合并成一个代码块

- public static void main(String[] args) {
- Student student = new Student("张三",10);
- System.out.println(student);
- }
打印结果是
![]()
为什么会打印粗这样的结果呢?
我们control点进去println底层

valueOf底层

toString底层

我们完全可以再自己代码中重新写一个toString方法,这样系统不会调用底层的代码,而会先考虑你写的新方法
点击鼠标右键选择generate,点进去toString,系统帮你生成一段新的打印代码,利用这段代码就可以正常打印了
- @Override
- public String toString() {
- return "Student{" +
- "name='" + name + '\'' +
- ", age=" + age +
- '}';
- //return "lalala"//这样就可以打印lalala
- }
![]()
这种方法叫做重写
重载指在一个类中,具有多个相同名称的方法,他们的参数列表却不相同
(参数类型不同、参数个数不同甚至是参数顺序不同)
- //方法1
- public static int add(int a,int b) {
- return a+b;
- }
- //方法2
- public static int add(int a,int b,int c) {
- return a+b+c;
- }
- //方法3
- public static int add(int[] array) {
- int ret = 0;
- for (int x: array) {
- ret += x;
- }
- return ret;
- }
-
- public static void main(String[] args) {
- int[] array = {1, 2, 3, 4, 5};
- System.out.println(add(array));
- System.out.println(add(new int[]{1, 3, 4, 5, 6}));//匿名对象
- }
方法3还有一个更🐂的重载方法
- /**
- * ...可变参数
- * @param array
- * @return
- */
-
- public static int add(int... array){
- int ret = 0;
- for (int x: array) {
- ret += x;
- }
- return ret;
- }


这两个类都有相同的eat方法
每个动物都有这几个共同的特点,把这些类的共性进行抽取,放到特定的类中,从而达到代码的复用效果。这种类就叫继承
抽取新建共性类Animal
- class Animal{//抽取出来的类
- public String name;
- public int age;
- public void eat(){
- System.out.println(this.name + "正在吃饭");
- }
- }
![]()
这里的Dog跟Animal是is-a的关系,Dog称为子类或者派生类,Animal称为父类或者基类或者超类
extends是拓展的意思,也就是继承


在Animal类中,访问修饰限定符只能决定访问权限,不能决定能不能被继承
所以在后面的类中,虽然name不能被访问,但是是可以继承的,提供一个get方法就能访问
- class Base{
- public int a;
- public int b;
- }
- class Derived extends Base{
- public int c;
- public void method(){
- a = 1;
- b = 2;
- c = 3;
- }
- }
- public class Test2{
- public static void main(String[] args) {
- Derived derived = new Derived();
- }
- }

这样的访问是不受限制的
- class Base{
- public int a = 9;
- public int b = 99;
- }
- class Derived extends Base{
- public int a = 88;
- public void method(){
- System.out.println("a: " + a);
- System.out.println("b: " + b);
- }
- }
打印结果:

当父类和子类的成员变量同名时,在子类当中使用时,优先打印子类的成员变量
总结:成员变量访问遵循就近原则,自己有优先自己的,没有再从父类中找
跟上面的成员变量访问差不多
super在子类方法中访问父类的成员变量和方法
回到刚才的Animal类中,构造Animal类的构造方法

Dog报错了,因为子类没有初始化父类成员,子类在构造完之前,一定要帮助父类进行初始化

圆圆和10就是对name和age的初始化
或者这样写(generate-->constructor)
- public Dog(String name, int age){
- super(name, age);
- }
super()调用父类的构造方法,帮助初始化子类从父类继承过来的成员,并不会生成父类对象
- class Animal{//抽取出来的类
- public String name;
- public int age;
- public void eat(){
- System.out.println(this.name + "正在吃饭");
- }
-
- static {
- System.out.println("Animal::static{静态}");
- }
- {
- System.out.println("Animal::{实例}");
- }
- public Animal(String name, int age) {
- this.name = name;
- this.age = age;
- System.out.println("Animal(String name, int age)");
- }
- }
- //拓展
- class Dog extends Animal{//狗继承了Animal
- static{
- System.out.println("Dog::static{静态}");
- }
- {
- System.out.println("Dog::{实例}");
- }
- public Dog(String name, int age){
- super(name, age);
- System.out.println("Dog(String name, int age)");
- }
- public void bark() {
- System.out.println(this.name+ " 正在旺旺叫!");
- }
- }
- class Test{
- public static void main(String[] args) {
- Dog dog = new Dog("圆圆",10);
- }
- }
打印结果是什么呢?

两个静态优先执行,父类再执行,子类最后执行
再多两行代码
- System.out.println("================");
- Dog dog2 = new Dog("圆圆",10);
静态的就不执行了

- package demo;
-
- public class Test3 {
- protected int a = 19999;
- }

为什么不能打印a,就算调用super也不行呢?
因为demo2里面的Test既不是Test3的子类,又与Test3处于不同包
看回之前的那张表,这张表在我一篇博客提过

此时protected正好满足第四类的限制
这个表格的前提继承的父类是由public修饰的
注意:这里的public不能替换成private或者protected


1.现在有一需求:当集成到某个层次上之后我们不再继承了

final是密封类,表示当前不能继承了。
final的另一个用法。下面的a只能初始化一次,成为了常量,恒等于199,再次初始化会报错。
- final int a = 199;
- a = 20;//err
- System.out.println(a);
final代表不可变 e.g String

2.组合(has-a/a part of关系)
- class Tire{
- // ...
- }
- // 发动机类
- class Engine{
- // ...
- }
- // 车载系统类
- class VehicleSystem{
- // ...
- }
- class Car{
- private Tire tire; // 可以复用轮胎中的属性和方法
- private Engine engine; // 可以复用发动机中的属性和方法
- private VehicleSystem vs; // 可以复用车载系统中的属性和方法
- // ...
- }
- // 奔驰是汽车
- class Benz extend Car{
- // 将汽车中包含的:轮胎、发送机、车载系统全部继承下来
- }
同一个人对待不同人表现出不一样的形态,这可以理解为最基本的多态
条件:
1.继承关系上-->向上转型
2.子类和父类有同名的覆盖/重写方法
3.通过父类对象的引用去调用这个重写的方法

常见的可以发生线上转型的三个时机
- //1.直接赋值
- /*Dog dog = new Dog("圆圆",10);
- animal这个引用指向dog这个引用所指向的对象;
- Animal animal = dog;*/
- Animal animal = new Dog("圆圆",10);
- //2.方法的参数,传参的时候进行向上转型
- public static void func1(Animal animal){
-
- }
- public static void main(String[] args) {
- Dog dog = new Dog("圆圆",10);
- func1(dog);
- }
- //3.返回值向上转型
- public static Animal func2(){
- Dog dog = new Dog("圆圆",10);
- return dog;
- }
当我们在Dog类中加入eat()方法
- public void eat(){
- System.out.println(this.name + "正在吃狗粮!");
- }
- //Animal
- public void eat(){
- System.out.println(this.name + "正在吃饭");
- }
- //Test
- public static void main(String[] args) {
- Animal animal = new Dog("圆圆",10);
- animal.eat();
- }
打印结果是

这两个eat方法满足方法返回值一样,方法名一样,方法的参数列表一样
在继承关系上,这两个方法关系是重写
但是在调用的时候忽然变成了调用子类的eat,这个过程叫做动态绑定
子类如果有eat方法那就调用子类的,子类没有就调用父类的

程序在编译的时候,确实调用的是Animal的eat
程序在运行时,调用了Dog的eat方法,是在这个时候才绑定方法(区分静态绑定)
静态绑定是什么呢?
像这种在编译的时候已经确定了调用谁,就是静态绑定
我们在写重写时,一般要在上面加一个注释,这个注释可以帮助你避免一些错误

实现重写:
1.最基本的返回值,参数列表和方法名必须是一样的
2.被重写的方法的访问修饰限定符在子类中要大于等于父类的
访问修饰限定符大小关系:private>default>protected>public
3.被private,static,final修饰的方法和构造方法是不能被重写的
4.被重写的方法返回值类型可以不同,但必须有父子关系,比如:

这种叫做协变类型
重写快捷方法,鼠标右键选generate,选择要重写的方法


- public static void eatFun(Animal animal){
- animal.eat();
- }
-
- public static void main(String[] args) {
- Dog dog = new Dog("圆圆", 19);
- eatFun(dog);
-
- Cat cat = new Cat("咪咪", 1);
- eatFun(cat);
- }
- //圆圆正在吃狗粮!
- //咪咪 正在吃猫粮!
当父类引用的子类对象不一样的时候,调用这个重写的方法,所表现出来的行为是不一样的!我们把这种思想叫做多态

我们知道dog和cat都属于animal,但是反过来,animal就一定是dog吗,这里拿一个大类去调用一个小类的方法明显不可能

我们需要强制转换类型,这也叫做向下转型,但是这种转型非常不安全

![]()
当我们使用cat类的时候就发现类型转换异常,就算强行转换也无济于事
那我如何避免用错类来向下转型呢?我们可以用instanceof
- if (animal instanceof Cat) {
- Cat cat = (Cat) animal;
-
- cat.miaomiao();
- }else{
- System.out.println("好吧!");
- }
- class Rect extends Shape{
-
- @Override
- public void draw(){
- System.out.println("矩形");
- }
- }
-
- class Triangle extends Shape{
- @Override
- public void draw() {
- System.out.println("🔺!");
- }
- }
-
- class Cycle extends Shape{
- @Override
- public void draw() {
- System.out.println("圆形!");
- }
- }
-
- public class Test {
- public static void main(String[] args) {
- Cycle cycle = new Cycle();
- Rect rect = new Rect();
- Triangle triangle = new Triangle();
- String[] strings = {"cycle","rect","cycle","rect","triangle"};
- for(String x :strings){
- if(x.equals("cycle")){
- cycle.draw();
- }else if (x.equals("rect")){
- rect.draw();
- }else if (x.equals("triangle")){
- triangle.draw();
- }
- }
- }
- }
如果有大量的条件和循环语句,这种代码拥有圈复杂度,我们可以用多态来降低这种复杂度
- public static void drawMap(Shape shape){
- shape.draw();
- }
-
- public static void main(String[] args) {
-
- Shape[] shapes = {new Cycle(), new Rect(), new Cycle(), new Rect(), new Triangle()};
- for(Shape shape: shapes){
- shape.draw();
- }
-
- }
这段代码的扩展能力非常强,在上方添加一个类下面就能给你画出来