目录
包
自定义包
包的访问权限控制
常见的包
Static成员
静态成员变量
静态成员方法
代码块
构造块
静态块
重写
继承
继承是啥?
父类成员访问
子类中访问父类成员变量
两者不同名
两者同名
子类中访问父类对的成员方法
super
子类构造方法
this和super
代码块再解读
protected关键字
继承方式
多态
向上转型:把子类所引用的对象给到父类
再说重写
向下转型
多态的好处
包
自定义包
选中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中的变量。
常见的包
Static成员
静态成员变量
假设学生都在一个教室上课
我们就把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
super在子类方法中访问父类的成员变量和方法
子类构造方法
回到刚才的Animal类中,构造Animal类的构造方法
Dog报错了,因为子类没有初始化父类成员,子类在构造完之前,一定要帮助父类进行初始化
圆圆和10就是对name和age的初始化
或者这样写(generate-->constructor)
public Dog(String name, int age){
super(name, age);
}
super()调用父类的构造方法,帮助初始化子类从父类继承过来的成员,并不会生成父类对象
this和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);
静态的就不执行了
protected关键字
package demo;
public class Test3 {
protected int a = 19999;
}
为什么不能打印a,就算调用super也不行呢?
因为demo2里面的Test既不是Test3的子类,又与Test3处于不同包
看回之前的那张表,这张表在我一篇博客提过
JAVA类和对象_cx努力编程中的博客-CSDN博客
此时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();
}
}
这段代码的扩展能力非常强,在上方添加一个类下面就能给你画出来