Java面向对象三大特性封装、继承、多态(超详细)


面向对象

这里就不解释什么是面向对象了

:加载局部变量;:new出来的结构:对象、数组;方法区:类的加载信息、静态域、常量池

堆(可回收垃圾的堆)

此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存(所有的对象实例以及数组都要在堆上分配)(我们new出来的结构数组对象啥的加载在堆空间中,对象的属性(非static的))加载在堆空间中。但对象使用完毕后,JVM虚拟机会标记成可回收的,等到内存不足的时候才会回收空间,让空间再次被利用

虚拟机用于存放局部变量,各种数据类型。方法执行完后自动释放。

方法区

用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据(类的加载信息、常量池、静态域)

在这里插入图片描述

Java基本认识

  • 属性=成员变量=field=字段、域
  • 方法=成员方法=函数=method
  • 创建类的对象=类的实例化=实例化类

类和对象

类就是某些具体事物的集合,假如说人(这个是类),这个人的有名字、年龄、独有的DNA或者穿着(这些都可以看成是类的属性)这个人有自己的行为,可以走路,吃饭(这些可以看成类的方法);面向对象程序设计重点是类的设计(类的成员设计)

对象

就是类的实例化(类是对象的蓝图)

  • 创建类,设计类的成员
  • 创建类的对象(类的实例化)
  • 通过"对象.属性"或”对象.方法"调用对象的结构
  • main里面定义方法,不能定义类(main可以定义多个类,但最多只能有一个被public修饰的class定义在main外面的类只能有一个被public修饰的class))如果在main外面定义类,要用该类的变量名来声明(方法不能单独定义在main外面)
    在这里插入图片描述

:有属性和方法。属性对应类中的成员变量;而行为(方法体现)对应类中的成员的方法

//Person是类
class Person {
    
    
    //下面是属性
    String name;
    int age;
    boolean isMale;
    //方法
    void eat() {
    
    
        System.out.println("吃饭");
    }
    void sleep() {
    
    
        System.out.println("睡觉");
    }
    void talk(String communicate){
    
    //这里的communicate是形参,也是局部变量
        System.out.println("说"+communicate+"门子话");
    	}
}
public class Zhangsan{
    
    
    public static void main(String[] args) {
    
    
        
        //创建Person类的对象(类的实例化)
        Person p=new Person();
        
        //调用对象的结构,属性、方法。调用属性:对象.属性
        p.name="张三";
        p.isMale=true;
        p.age=20;
        System.out.println(p.name);//输出结果:张三
        System.out.println(p.age);//输出结果:20
        
        //调用方法:对象.方法
        p.eat();
        p.sleep();
        p.talk("Chinese");
        
        Person p2=new Person();
        p2.age=30;
        System.out.println(p2.name);//输出结果:null
        System.out.println(p2.age);//输出结果:30
        
        Person p3=p; 
        System.out.println(p3.name);//输出结果:张三
         //将p变量保存的对象地址赋给p3,导致p和p3指向了堆空间的同一个对象实体
    }
}

属性(成员变量)和局部变量区别:

相同点:定义的格式差不多,先声明,后使用;变量都有其对应作用域

不同点:在类中声明的位置不同,属性定义在类的一对{}进行的。

局部变量:声明在方法内、方法形参、代码块内、构造器形参、构造器内部的变量;局部变量没有初始化值,调用之前一定要显示赋值

权限修饰符

privatedefault(一般省略)publicprotected

(Java规定4种常用权限修饰符:privatepublic(权限最大)、defaultprotected(体现结构被调用权限大小)(封装这里才了解,声明属性一般用缺省))

默认初始化值:

类的属性,根据其类型,都有默认初始化(跟数组那块差不多)

整型:0
浮点型:0.0
booleanfalse
字符型:0'\u0000')
引用数据类型:null

匿名对象

顾名思义,匿名就是没有名字的对象,在创建对象时,只通过new的动作在堆内存开辟空间,却没有把堆内存空间的地址值赋值给栈内存的某个变量用以存储

Person p=new Person();//有名字的对象
new Person();//没有名字的对象

匿名对象的使用(只能调用一次

new Student().方法1();
new Student().方法2();
//这里有区别,在堆空间相当于new了2个对象;
new Student().方法1()=1999;//(输出结果为1999)
new Student().方法2();//这里调用的方法没有赋值(输出结果为0.0)

总结

  • 如果创建一个类的多个对象,则每个对象都有自己独立的拥有一套类的属性。(非static的)如果修改一个对象的属性a,则不影响另外一个对象属性a的值。属性是加载到堆空间中(非static),而局部变量是加载到栈空间中(简单来说就是每new出来的对象都有属于它自己的一套属性)
  • 匿名对象其实就是对象,对象具有的功能匿名对象都具有,只不过有名字的对象可以重复使用,匿名对象只能用一次罢了
public class PersonTest{
    
    
	public static void main(String [] args){
    
    
        method(new Student());//匿名对象
}
        public static void method (Student s){
    
    
    }
}

方法

方法:将具有独立的代码块组织成为一个整体,使具有特殊功能的代码集。

//方法定义格式:权限修饰符 返回值类型 方法名(形参列表)
    public (static) void 方法名(){
    
    方法体}
/*
带参数:public (static) void 方法名(参数){}
单个参数:public (static) void 方法名(数据类型 变量名){}
*/

方法调用

public class Person{//创建一个类
    public void ZhangSan(){
		sout("我是张三");
    }
}
public static void main(String{} agrs){
    Person p=new Person();//类的实例化
    p.Zhangsan();//调用该方法;输出结果:我是张三
}

注意

方法不能嵌套定义;如果方法定义有返回值的话,要在方法声明时,指定返回值的类型。同时,需要使用return关键字来返回指定类型的变量或者常量;方法可以相互调用,也可以调用当前类的属性或方法

方法重载

在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同即可(与返回值类型无关,只看参数列表,且参数类表必须不同(参数个数或者参数类型)调用时,根据方法参数列表的不同来区别)

//方法重载(同类同名参数不同(跟方法的权限修饰符、返回类型、形参变量名、方法体无关))
public void arr(int i,int j){
    
    
    
}
public void arr(double i,double j){
    
    
    
}

可变个数形参的方法

可变个数形参的方法定义参数必须定义在末尾,因为长度不确定,且最多只能声明一个形参,允

许直接定义能和多个实参相匹配的形参。从而可以用更简单的方法,来传递个数可变的实参

(可变个数形参的方法与本类中方法名相同,形参不同之间的方法之间构成重载)

//可变个数的形参格式:
public void show(String ... strs){
    
    
}
public void show(int a,String ... strs){
    
    
    //有多个参数的时候,定义可变个数形参必须定义在末尾
}
public void show(String ... strs){
    
    
}//等价于
public void show(String[] strs){
    
    
}
//这里如果执行的时候会报错,原因:历史原因。方法定义只能定义其中一个

方法参数的值传递机制

方法由其所在类或对象调用才有意义。若方法含有参数(形参:方法声明时的参数;实参:方法调用时实际传给形参的参数值)

class arr{
    
    
    int number;
}
public class ZhangSan {
    
    
    public static void main(String[] args) {
    
    
        arr p=new arr();
        p.number = 1001;
        arr p1 = p;
        System.out.println("number="+p.number+" p1="+p1.number);
        p1.number = 1002;
        System.out.print("number="+p.number+" p1="+p1.number);
    }
}
//输出结果:
//number=1001 p1=1001
//number=1002 p1=1002

注意

如果参数是基本数据类型,此时实参赋给形参的是实参真实存储的数据值;跟引用数据类型不同,引用数据类型是在堆空间new一个空间,然后把地址赋给另一个变量,另一个变量接收到的是同一个地址,所以两个同时指向同一块空间,修改变量的值也自然是同一块地址里面的变量(简单来说基本数据类型传递的是变量的数据值传递给形参,而引用数据类型传递的是变量的地址值传递给形参)

//基本数据类型
//import java.util.Scanner;
public class ZhangSan {
    
    
    public static void main(String[] args) {
    
    
        int number=10;
        int score=20;
        ZhangSan p=new ZhangSan();//造对象
        p.swap(number,score);//然后调用方法
        System.out.println("number="+number+"  成绩="+score);
        //输出结果:number=10   成绩=20
    }
    void swap(int i,int j){
    
    
        int temp=i;
        i=j;
        j=temp;
    }
}
//引用数据类型
public class ZhangSan {
    
    
    public static void main(String[] args) {
    
    
        Fen p=new Fen();
        p.x=10;
        p.y=20;
        new ZhangSan().swap(p);//匿名对象(注意只能调用一次,不能多次使用)
        System.out.println("x="+p.x+"  y="+p.y);
        //输出结果:x=20  y=10

    }
    public void swap(Fen p1){
    
    
        int temp= p1.x;
        p1.x=p1.y;
        p1.y=temp;
    }
}
class Fen{
    
    
    int x,y;
}
class Circle{
    
    
    double time;
    public double findArea(){
    
    
        return Math.PI*time*time;
    }
}
class PassObject {
    
    
    public void printAreas(Circle c, int time) {
    
    
        System.out.println("Radius\t\tArea");
        for (int i = 1; i <= time; i++) {
    
    
            c.time=i;
            System.out.println(c.time+"\t\t"+c.findArea());
        }
    }
}
public class ZhangSan {
    
    
    public static void main(String[] args) {
    
    
        PassObject p = new PassObject();
        //System.out.println("Radius\t\tArea");
        Circle d=new Circle();
        p.printAreas(d,5);
    }
}
//输出结果:
Radius		Area
1.0		3.141592653589793
2.0		12.566370614359172
3.0		28.274333882308138
4.0		50.26548245743669
5.0		78.53981633974483

递归方法

递归方法就是自己调用自己,递归函数有返回值的比没有返回值的麻烦一点,因为一个函数只有一个返回值,但是递归还要求有基础情形的存在,所以还必须有if判断来终止递归。所以在每一个if或者else后边都有一个return,这样保证函数在任何一种情况下都有且仅有一个返回值。

//自己调用自己
public class ZhangSan {
    
    
    public static void main(String[] args) {
    
    
        ZhangSan p=new ZhangSan();
        System.out.println(p.sum(100));
    }
    public int sum(int i){
    
    
        if(i==1){
    
    
            return 1;
        }else return i+sum(i-1);
    }
}

封装

封装性的体现

封装性的体现就是通过privatepublic去隐藏(属性)或者调用,如果在类中属性为private,外部类或者方法是不能调用的,只能是该类内部调用,对应的也提供了setXxx()getXxx()方法来访问

public class EncapTest{
    
    
   private int age;//设置为私有的
   //提供公共的public方法
   public void setAge(int a){
    
    //设置赋值有参数
		age=n;
}
   public int getAge(){
    
    
      return age;
   }
}

4种权限修饰符

权限大到小排序(都可以修饰类内部结构:属性、方法、构造器、内部类)修饰类的话只能使用缺省和public

  • private(类内部)(权限最小)
  • default(同一个包或者类内部)(缺省:没有权限修饰符)
  • protected(同一个包或者类内部或者不同包的子类)
  • public(权限最大)

构造器

构造器(constructor)

构造器的权限跟类相同,任何一个类都有构造器,构造器的作用就是造对象和初始化对象的属性。

  • 构造器必须与类同名(如果一个源文件中有多个类,那么构造器必须与公共类同名)
  • 每个类可以有一个以上的构造器(构造器可以有多个不同参数)
  • 构造器:不能用abstractfinalnativestatic修饰(没有意义)
  • 构造器没有返回值
  • 构造器总是伴随着对象的实例化一起调用
  • 不添加任何构造器会默认有空的构造器
  • 如果编写时添加了有参构造方法而未添加无参构造方法,那么编译器只认有参构造方法而不会默认添加无参构造方法!
//构造定义格式如下:
//权限修饰符 类名(形参列表){
    
    
//}
class Person {
    
    
    String name;
    int age;
    public Person(){
    
    //这里是一个空的构造器,一般情况下使用public方法权限
    }
}
public class ZhangSan{
    
    
    public static void main(String[] args) 
        Person p=new Person();
    //造对象,其实这里就是new+构造器(Person());构造器的作用就是造对象(类的实例)
    }
}

构造器总是伴随着对象的实例化一起调用

package test;

public class Fu {
    
    
    public Fu(){
    
    
        System.out.println("你好");
    }
}

package test;

public class GzqText {
    
    
    public static void main(String[] args) {
    
    
       Fu p=new Fu();
    }
}
//输出结果:你好

初始化对象的属性

class Person {
    
    
    private double base;
    private double height;
    //构造器
    public Person(){
    
    
    }
    public Person(double b,double h){
    
    //带参数的构造器
        base=b;
        height=h;
    }
    //封装
    public void setBase(double b) {
    
    
        base = b;
    }
    public double getBase() {
    
    
        return base;
    }
    
    //封装
    public void setHeight(double h) {
    
    
        height = h;
    }
    public double getHeight() {
    
    
        return height;
    }
}
public class StudentTest {
    
    
    public static void main(String[] args) {
    
    
        Person p=new Person(2.4,3.5);
        System.out.println(p.getBase()+" "+p.getHeight());
    }
}
//输出结果:2.4 3.5

注意

如果没有显式的定义类的构造器的话,则系统默认提供一个空参的构造器(一旦我们显式的定义类的构造器之后,系统就不再提供默认的空参构造器)如果一个类中有多个构造器,彼此称为重载

JavaBean

JavaBean是一种Java语言写成的可重用组件。满足类是公共的、有一个无参的公共的构造器、有属性,且有对应的getset方法(一个类里包括只属性和setget方法时,简称javaBean,别名POJOVOTO)这里简单了解一下就行

public class Person {
    
    
    private int id;
    private String name;
    public Person(){
    
    
    }
    public void setId(int i) {
    
    
        id = i;
    }
    public int getId() {
    
    
        return id;
    }

    public void setName(String n) {
    
    
        this.name = n;
    }
    public String getName() {
    
    
        return name;
    }
}

this关键字

  • 在构造方法中this关键字代表当前正在构造对象
  • 在成员方法中this关键字指正在调用该方法的对象
package test;
public class GzqText {
    GzqText(){
        System.out.println("构造方法中:this="+this);
    }
    void show(){
        System.out.println("成员方法中:this="+this);
    }
    public static void main(String[] args) {
       GzqText p=new GzqText();
       p.show();
        System.out.println("main方法中:p="+p);
    }
}
/*输出结果:
构造方法中:this=test.GzqText@15aeb7ab
成员方法中:this=test.GzqText@15aeb7ab
main方法中:p=test.GzqText@15aeb7ab
*/
  • 区分成员变量和局部变量
//this修饰的变量用于指代成员变量;方法形参如果和成员变量同名的话,不带this修饰的变量指的是形参而不是成员变量(可以在方法或者构造器使用,this只能用在构造器或者方法中,用于获得调用当前的构造器方法的对象引用。可以和任何的对象引用一样来处理这个this对象。)
public class Person {
    
    
    private int id;
    public Person(){
    
    
    }
    
    public void setId(int id) {
    
    //这里指的是局部变量
        id = id;//这种是错误的
        this.id=id;//这里的id指代的是成员变量
    }
    public int getId() {
    
    
        return id;
    }
    
/*
当一个对象创建后,Java虚拟机JVM就会给这个对象分配一个引用自身的指针,这个指针的名字就是 this。成员变量和局部变量同名的时候使用,解决局部变量隐藏成员变量(代表所在类的对象引用)在类(构造器)的方法中,我们可以使用this.属性或者方法的方式,调用当前对象属性或者方法。因此,this只能在类中的非静态方法中使用,静态方法和静态的代码块中绝对不能出现this,并且this只和特定的对象关联,而不和类关联,同一个类的不同对象有不同的this
*/

this()调用构造器

//this(形参列表)方式,调用本类中指定类的其他构造器
package test;
class Use{
    
    
    String name;
    int id;
    int age;
    public Use(String name){
    
    
        this(name,19,45);
        this.name=name;
        System.out.println("构造器1已调用");
    }
    public Use(String name,int id,int age){
    
    
        this.name=name;
        this.age=age;
        this.id=id;
        System.out.println("构造器3已调用");
    }
   public Use(String name,int age){
    
    
        this("张三");
       System.out.println("构造器2已调用");
    }
    @Override
    public String toString() {
    
    
        return "Use{" +
                "name='" + name + '\'' +
                ", i=" + id +
                ", age=" + age +
                '}';
    }
}
public class GzqText {
    
    
    public static void main(String[] args) {
    
    
        Use p=new Use("小明",45);
        System.out.println(p);
    }
}
/*
构造器3已调用
构造器1已调用
构造器2已调用
Use{name='张三', i=19, age=45}
*/

java包(package)

在开发过程中,会定义很多类,为了避免相同类名称出现而发生覆盖的情况,把所有java程序保存在各自的目录里面,而该目录就是包。包的本质实际上就是一个文件夹。命名包的时候要注意尽量避开系统冲突的名字,例如java.lang

/*定义包
package 包名称
*/
package test;//这里就是包
public class GzqText {
    
    
    GzqText(){
    
    
        System.out.println("构造方法中:this="+this);
    }
    void show(){
    
    
        System.out.println("成员方法中:this="+this);
    }
    public static void main(String[] args) {
    
    
       GzqText p=new GzqText();
       p.show();
        System.out.println("main方法中:p="+p);
    }
}

import关键字

import关键字使用

  • import(导入)在源文件显式的使用import结构导入指定包下的类、接口。如果使用的类或接口是java.lang包下定义的就不用导,可以省略import
  • import static:导入指定类或接口中的静态结构:属性或方法
  • 使用xxx.*方式表明可以调用xxx包下的所有结构,但是如果使用的是xxx子包下的结构,则仍需要显式导入(跨包就要import)因为包之间会存在相互调用的情况,一个包不但会调用本包中类,还会调用其他包中的类,所以需要用关键词import进行导入包中的类,格式:import 包名称.类名称;

导包的格式

import 包名.类名;
范例:
import myClass.cjjy_0.StudentTest
import java.util.Scanner;
import java.util.Arrays
/*
如果源文件中,使用了不同包同名的类想导的话,则必须至少有一个类需要以全类名的方式显式,如下:
myClass.cjjy_0.StudentTest.p=new myClass.cjjy_0.StudentTest
/*

包与包之间也有权限

范围 private default protected public
1 同一个包中的同一个类
2 同一包中的不同类 -
3 在不同包中的子类 - -
4 在不同包中的非子类 - - -

系统常用包(这里举几个例子)

java.lang/java.util/java.awt.javax.swing等

继承

继承

是面向对象三大特征之一,一旦子类继承父类的话,子类就有父类声明的所有属性和方法还可以在子类中重新定义,追加属性和方法。

继承的格式

public class 子类名(子类也叫派生类、subclass) extends 父类名(父类也叫超类、基类、superclass){
    
    
}
//格式:
class B{
    
    
}
class A extends B{
    
    
}
//extends关键字用于继承,中文意思延伸,可以理解为,class A extends B;表示A 是B的延伸(子类)
//企鹅
public class Penguin {
    
     
    private String name; 
    private int id; 
    public Penguin(String myName, int  myid) {
    
     
        name = myName; 
        id = myid; 
    } 
    public void eat(){
    
     
        System.out.println(name+"正在吃"); 
    }
    public void sleep(){
    
    
        System.out.println(name+"正在睡");
    }
    public void introduction() {
    
     
        System.out.println("大家好!我是"         + id + "号" + name + "."); 
    } 
}
//老鼠
public class Mouse {
    
     
    private String name; 
    private int id; 
    public Mouse(String myName, int  myid) {
    
     
        name = myName; 
        id = myid; 
    } 
    public void eat(){
    
     
        System.out.println(name+"正在吃"); 
    }
    public void sleep(){
    
    
        System.out.println(name+"正在睡");
    }
    public void introduction() {
    
     
        System.out.println("大家好!我是"         + id + "号" + name + "."); 
    } 
}
//父类
public class Animal {
    
     
    private String name;  
    private int id; 
    public Animal(String myName, int myid) {
    
     
        name = myName; 
        id = myid;
    } 
    public void eat(){
    
     
        System.out.println(name+"正在吃"); 
    }
    public void sleep(){
    
    
        System.out.println(name+"正在睡");
    }
    public void introduction() {
    
     
        System.out.println("大家好!我是"         + id + "号" + name + "."); 
    } 
}
//Penguin继承父类Animal
public class Penguin extends Animal {
    
     
    public Penguin(String myName, int myid) {
    
     
        super(myName, myid); 
    } 
}
//Mouse继承父类Animal
public class Mouse extends Animal {
    
     
    public Mouse(String myName, int myid) {
    
     
        super(myName, myid); 
    } 
}

继承可以分为单一继承、间接继承和分层继承

//单一继承
class B{
    
    
}
class A entends B{
    
    
    
}

//间接继承(多级继承)
class A{
    
    
}
class B entends A{
    
    
    
}
class c entends B{
    
    
    
}

//分层继承
class A{
    
    
}
class B entends A{
    
    
    
}
class c entends A{
    
    
    
}
//注意:Java中的类不支持多继承(不允许一个类继承多个父类)
/*
Java 的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如 B 类继承 A 类,C 类继承 B 类,所以按照关系就是 B 类是 C 类的父类,A 类是 B 类的父类,父类可以派生出多个子类,子类继承父类以后,有直接父类和间接父类,就获取了直接父类包括间接父类中声明的属性和方法
*/

继承的特性

  • 子类拥有父类非静态的属性、方法,但不会继承静态的方法和属性。继承下来的方法可以被覆盖掉,实例变量不行
  • 因为封装性的影响,使得子类不能直接调用父类的结构而已
  • 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
  • 子类可以用自己的方式实现父类的方法。
  • 继承是所有OOP语言和Java语言不可缺少的组成部分。当创建一个类时,总是在继承,因此,除非已明确指出要从其他类中继承,否则就是在隐式地从Java的标准根类Object进行继承(java.lang.Object所有类的父类)

在这里插入图片描述

extends关键字

在 Java 中,类的继承是单一继承,也就是说,一个子类拥有一个父类,所以子类extends只能直接或者间接继承一个父类,在类的声明中,通过关键字extends来创建一个类的子类,extends 是继承某个类, 继承之后可以使用父类的方法, 也可以重写父类的方法;

public class Animal {
    
     
    private String name;   
    private int id; 
    public Animal(String myName, String myid) {
    
     
        //初始化属性值
    } 
    public void eat() {
    
      //吃东西方法的具体实现  } 
    public void sleep() {
    
     //睡觉方法的具体实现  } 
} 
 
public class Penguin  extends  Animal{
    
    
    public void eat() {
    
    //重写方法} 
    public void sleep() {
    
    //重写方法} 
}

super关键字

我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。this关键字:指向自己的引用。如果想要在子类中调用父类被重写的方法时,则必须显式的使用super

super调用属性和方法
class Animal {
    
    
  void eat() {
    
    
    System.out.println("animal : eat");
  }
}
 
class Dog extends Animal {
    
    
  void eat() {
    
    
    System.out.println("dog : eat");
  }
  void eatTest() {
    
    
    this.eat();   // this 调用自己的方法
    super.eat();  // super 调用父类方法
  }
}
 
public class Test {
    
    
  public static void main(String[] args) {
    
    
    Animal a = new Animal();
    a.eat();
    Dog d = new Dog();
    d.eatTest();
  }
}
输出结果:animal : eat
animal : eat

super调用构造器

  • 在子类的构造函数中,必须调用父类的构造函数
  • 若子类构造函数当中没有主动调用父类的构造函数,编译器会默认加上一行super()来调用父类的构造函数
  • 若想调用父类中其他的构造函数,则根据super()的括号中的参数来决定
  • 子类只能继承父类的成员变量和成员函数,不能继承父类的构造函数
  • 子类可以调用父类的构造函数,且必须放在构造函数中的第一行代码,类似于this调用成员函数的规则
public Student(String name,int age,String major){
    super(name,age);//调用父类中传参的构造器
    this.major=major;
}

注意

在Java程序编写中,子类的构造方法必定会调用父类的构造方法,如果在子类的构造方法中没有指定调用父类的哪个构造方法,在实例化子类对象时,子类会默认调用父类的无参构造方法。如果在父类中没有定义无参构造方法的话,编译会报错。从另外一面说,子类是从父类继承而来,继承了父类的属性和方法,如果在子类中先不完成父类的成员的初始化,则子类无法使用,应为在 java 中不允许调用没初始化的成员。在构造器中是顺序执行的,也就是说必须在第一行进行父类的初始化。而 super 能直接完成这个功能。 this() 通过调用本类中的其他构造器也能完成这个功能。所以, this() 或者 super() 必须放在第一行。

package test;
public class Fu {
    
    
    public Fu(){
    
    
    }
    public Fu(int i){
    
    
        System.out.println(i);
    }
}
package test;
public class Gzq extends Fu{
    
    
    public Gzq(int i){
    
    
        super(4);
        System.out.println("i的值为:"+i);
    }
}
package test;

public class GzqText {
    
    
    public static void main(String[] args) {
    
    
        Gzq p=new Gzq(5);
    }
}
//输出结果为:4
//i的值为:5

子类实例化的过程

从结果上来看

子类继承父类后,就获取了父类中的所有属性和方法;创建子类的对象,在堆的空间中,也会加载所有父类声明的属性

从过程来看

当我们通过子类的构造器创建子类对象时,我们一定会直接或者间接调用其父类的构造器,进而调用父类的父类的构造器,正因为加载过所有父类的结构,所以才可以看到内存中有父类中的结构,子类才可以考虑调用。

方法重写

区别点 重载方法 重写方法
参数列表 必须修改 一定不能修改
返回类型 可以修改 一定不能修改
异常 可以修改 可以减少或删除,一定不能抛出新的或者更广的异常
访问 可以修改 一定不能做出更严格的限制(可以降低限制)

方法重写概念

  • 重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!(返回值类型:父类被重写的方法的返回值类型是void,则子类重写的方法的返回值也只能是void
  • 子类不能重写父类中声明为静态权限的方法(子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符,例如:如果父类的一个方法被声明为public,那么在子类中重写该方法就不能声明为protected)(方法重写子类和父类权限修饰符要相同)
package exer_0;

class Exer {
    
    
    public void speak(){
    
    
        System.out.println("我是张三");
    }
}
package exer_0;
public class ExerTwo extends Exer{
    
    //继承
    @Override//翻译:覆盖;用来检测重写是否报错
    public void speak() {
    
    
        super.speak();
        System.out.println("我连张三都不如");
    }
}
package exer_0;
import java.util.Scanner;
public class ExerTest {
    
    
    public static void main(String[] args) {
    
    
        Exer p=new Exer();
        p.speak();
        ExerTwo p1=new ExerTwo();
        p1.speak();
    }
}
/*
输出结果:
我是张三
我是张三
我连张三都不如
*/

多态性

多态的前提和体现

  • 多态的前提要类的继承(子父类关系)或者实现关系

  • 有父类引用指向子类对象的

  • 方法重写

    //定义的格式:
    父类类型 变量名=new 子类类型();
    

多态概念:

现实事物经常会体现出多种形态,如学生,学生是人的一种,则一个具体的同学张三既是学生也是,即出现两种形态。一个学生的对象便既是Student,又是Person。父类的引用指向子类的对象(或者子类的对象赋给父类的引用)

多态的使用

是编译看左边,运行看右边(调用的是子类重写父类的方法,不能调用子类自己定义过的方法)如果要调用子类自己定义的方法,得引用子类的对象去调用)

public class Person{
    
    
    public void eat(){
    
    
        sout("人,吃饭");
    }
    public void sleep(){
    
    
        sout("人,睡觉");
    }
}
public class Man extends Person{
    
    
    public void eat(){
    
    
        sout("人吃饭可以长肌肉");
    }
    public void sleep(){
    
    
        sout("人可以修仙");
    }
    public void palyGame(){
    
    
        sout("喜欢玩游戏")
    }
}
public class PolymorphismText{
    
    
    public static void main(String[] args){
    
    
        Person p=new Man();
        p.eat();//输出结果:人吃饭可以长肌肉
       	p.sleep();//输出结果:人可以修仙
        p.palyGame();//编译错误,原因是调用方法必须是子类重写父类的方法;不能调用子类里面自己定义的方法(除非这个是父类里面定义过的)
    }
}
public class Person{
    
    
    public void eat(){
    
    
        sout("人,吃饭");
    }
    public void sleep(){
    
    
        sout("人,睡觉");
    }
}
public class Man extends Person{
    
    
    public void eat(){
    
    
        sout("男人吃饭可以长肌肉");
    }
    public void sleep(){
    
    
        sout("男人可以修仙");
    }
    public void palyGame(){
    
    
        sout("喜欢玩游戏");
    }
}
public class PolymorphismText{
    
    
    public static void main(String[] args){
    
    
       PolymorphismText p2=new PolymorphismText();
    	p2.func(new Man());
    }
     public void func(Person p1){
    
    
         //这里相当于把Person p1=new Man();也就是多态的特点,只能调用子类重写父类的方法
         p1.eat();//所以输出结果:男人吃饭可以长肌肉
    }
}

对象的多态性只适用于方法,不适用属性

public class Exer{
    
    
    int i=1001;
    public void text(){
    
      
    }
}
public class Endtext extends Exer{
    
    
    int i=1002;
	public void text)(){
    
      
    }
}
public class PolymorphismText{
    
    
    public static void main(String[] args){
    
    
		PolymorphismText p2=new PolymorphismText();
		sout(p2.i);
        //输出结果是1001不是1002;对象的多态性只适用于方法,不适用属性
    }

虚拟方法

虚拟方法概念:子类中定义了与父类同名同参数的方法,在多态情况下,将此父类的方法称为虚拟方法,父类会根据给它的不同子类的对象去调用属于子类的该方法

instanceof关键字

用来判断某个对象是否属于某种数据类型。返回类型是布尔类型

多态转型和使用

  • 多态的转型分为向上转型和向下转型两种

  • 多态本身就是向上转型过的过程

  • 父类类型 变量名=new 子类类型();
    
  • 向下转型:一个已经向上转型的子类对象可以使用强制类型转换的格式,将父类引用类型转为子类引用各类型

    子类类型 变量名=(子类类型) 父类类型的变量;//下面有举例子
    

有了对象的多态性,内存中实际上加载了子类特有的属性和方法,但由于变量声明为父类类型,导致编译时只能调用父类声明的属性和方法,这时候如果想要调用子类特有的属性和方法,可以使用强制类型转换(向下转型),如下:

public class Person{
    
    
    public void eat(){
    
    
        sout("人,吃饭");
    }
    public void sleep(){
    
    
        sout("人,睡觉");
    }
}
public class Man extends Person{
    
    
    public void eat(){
    
    
        sout("人吃饭可以长肌肉");
    }
    public void sleep(){
    
    
        sout("人可以修仙");
    }
    public void palyGame(){
    
    
        sout("男生喜欢玩游戏")
    }
}
public class PolymorphismText{
    
    
    public static void main(String[] args){
    
    
        Person p=new Man();
        p.eat();//输出结果:人吃饭可以长肌肉
       	p.sleep();//输出结果:人可以修仙
        Man p1=(Man)p;//向下转型
        p1.palyGame();//输出结果是男生喜欢玩游戏
    }
}
//调用子类的方法

instanceof关键字使用格式

/*
instanceof关键字判断其左边对象是否为其右边类的实例,返回boolean类型的数据。可以用来判断继承中的子类的实例是否为父类的实现。使用instanceof关键字做判断时, instanceof 操作符的左右操作数必须有继承或实现关系。在判断某个类(接口也可以看成一个特殊的类)的对象是不是其他类(或接口)的实例,一定要首先进行向上转型,然后才可用instanceof关键字进行判断,这是基本操作规范。
*/
package test;
class Mul{
    
    

}
class A extends Mul{
    
    

}
class B extends Mul{
    
    

}

public class GzqText {
    
    
    public static void main(String[] args) {
    
    
        Mul p=new A();
        System.out.println(p instanceof A);//输出结果:true
        System.out.println(p instanceof B);//输出结果:false
    }
}
/*总结:p instanceof A 判断p是不是A的实例,是的话返回boolean类型;如果这里(p instanceof A )把A换成Mul输出结果也是true
*/

equals关键字

equals()(方法)不同于==(运算符),==使用于基本数据类型和引用数据类型,基本数据类型是比较他们的值,引用型数据类型比较的是它们的地址;像StringDateFile包装类都重写了Object类中的equals()方法。重写以后,就是比较的不是2个地址是否相同,而是比较2个对象的“实体内容”是否相同。通常情况下,我们自己如果要使用equals()方法的话,其实比较的是2个对象地址是否相同,如果要比较实体内容是否相同的话就需要对Object类中的equals()进行重写

int x = 10;
int y = 10;
String str1 = new String("abc");
String str2 = new String("abc");
System.out.println(x == y);//输出结果:true
System.out.println(str1 == str2);//输出结果:false;原因是存放是两个不同的"abc"这个对象的地址值,两个对象的地址不同,所以是false

String s1 = "abc";
String s2 = "abc";
System.out.println(s1 == s2);//输出结果:true
System.out.println(s1.equals(s2));//输出结果:true
//2者都是true,因为我们的程序在向虚拟机的常量池申请内存的时候,会先判断常量是不是已经在常量池中存在了;第一行代码s1申请的时候,常量并未存在,所以会申请一块新的内存,第二行代码s2在申请的时候已经存在了该常量,所以会直接指向刚才那一块内存,所以s1和s2不仅值相同,地址也是一样的

注意

Java中Object类是所有类的父类,它里面定义了equals()方法:这个关键字是属于Object这个超类的,它只适用于引用型数据类型,它比较的是它们内容是不是相等(不能使用一个值为null的引用类型变量来调用非静态方法,否则会抛出异常)(想更详细了解可以去查看Object源码)参考了Object的源码我们可以发现,equals方法跟==的作用是相同的,StringDate等都重写了Object类中的equals()的方法,重写以后就是比较2个对象的实体内容的是否相同,而==比较的是地址是否相等,equals()方法比较的是内容是否相等

String string1="加油";
String string2=new String("加油");
System.out.println(string1==string2);
//输出结果:false,这一行代码会在Java虚拟机常量池当中生成
System.out.println(string1.equals(string2));
//输出结果:true,而这一行代码会在Java虚拟机的堆当中生成一个String的对象

(在实际开发中,自己定义的类必须重写equals方法还有hashCode方法,不然的话==equals意义是相同的)

Object中的equals()定义

public boolean equals(Object obj){
    
    
    return (this==obj);
    //比较地址是否相同;这里this是谁调用这个equals,this就是那个对象
}
//说明:Object类中定义的equals()和==的作用是相同的,比较2个对象的首地址是否相同(即判断它们是不是指向同一个对象实体)
public boolean equals(Object anObject) {
    
    
        if (this == anObject) {
    
    
            return true;
        }
        if (anObject instanceof String) {
    
    
            String aString = (String)anObject;
            if (!COMPACT_STRINGS || this.coder == aString.coder) {
    
    
                return StringLatin1.equals(value, aString.value);
            }
        }
        return false;
    }

总结:

如果要自定义的类要使用equals()的话,也通常是比较两个对象的"实体内容"是否相同,如果要使用equals()就得对Object中的equals()方法重写

//重写规则(手动实现重写):比较2个对象的实体内容是否相同,对Object里面的equals()进行重写,如下:   
package exer_0;
import java.util.Objects;
public class Exer{
    
    
    private int orderld;
    private String orderName;
    public Exer(int orderld, String orderName) {
    
    
        this.orderld = orderld;
        this.orderName = orderName;
    }
    public void setOrderld(int orderld) {
    
    
        this.orderld = orderld;
    }
    public void setOrderName(String orderName) {
    
    
        this.orderName = orderName;
    }
    public int getOrderld() {
    
    
        return orderld;
    }
    public String getOrderName() {
    
    
        return orderName;
    }
    @Override
    public boolean equals(Object obj) {
    
    
        if(this==obj){
    
    
            return true;
        }
        if(obj instanceof Exer){
    
    
            Exer exer=(Exer)obj;
            return this.orderld==exer.orderld&&this.orderName.equals(exer.orderName);
        }
        return false;
    }
}
package exer_0;
public class ExerTest {
    
    
    public static void main(String[] args) {
    
    
        Exer p=new Exer(1002,"bb");
        Exer p1=new Exer(1002,"bb");
        System.out.println(p.equals(p1));
    }
}
//输出结果:true

补充一下

if(s.equals("aaa")){
    
    
    ...
}
//这样写其实没有问题,但是装了Alibaba Coding Guidelines这个插件后会报错,为什么呢?因为很多情况下,并不能保证字符串s是不是为null,即直接这么判断,很容易产生空指针异常的错误,因此正确的使用方法应该是:"aaa".equals(s);

toString()

我们先来看一段代码

package test;

public class ToText {
    
    
    public static void main(String[] args) {
    
    
        ToText p=new ToText();
        System.out.println(p);
        System.out.println(p.toString());
    }
}
/*
输出结果:
test.ToText@16b98e56
test.ToText@16b98e56
*/
//总结:我们在输出一个对象的时候,默认就是调用这个对象的toString方法

toString()的使用

  • 当我们输出一个对象引用时,实际上就是调用当前对象的tostring()(将对象转换成字符串,方便打印)
  • StringDateFile、包装类等重写了Object类中toString的方法,使得调用对象的toString()时。返回"实体内容"
  • 如果需要打印一个对象里面的信息的时候,可以去重写toString方法
package test;
public class Text {
    
    
    public static void main(String[] args) {
    
    
        Texto p=new Texto(5,"小明");
        System.out.println(p.toString());
    }
}
class Texto{
    
    
    int age;
    String name;

    public Texto(int age, String name) {
    
    
        this.age = age;
        this.name = name;
    }

    public void setAge(int age) {
    
    
        this.age = age;
    }
    public void setName(String name) {
    
    
        this.name = name;
    }
    public int getAge() {
    
    
        return age;
    }
    public String getName() {
    
    
        return name;
    }
    @Override
    public String toString() {
    
    
        return "Texto{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}
//输出结果:Texto{age=5, name='小明'}

总结

  • toString()方法在Object类里定义的,其返回值类型为String类型,返回类名和它的引用地址
  • 在进行String类与其他类型的连接操作时,自动调用toString()方法
  • 可以根据需要在用户自定义类型中重写toString()方法,如Stirng类重写了toString()方法,返回字符串的值
  • 基本数据类型转换为String类型是,调用了对应包装类的toString()方法(包装类在补充有说明)

static关键字

  • staticstatic静态的意思)可以修饰属性、方法、代码块、内部类、成员方法、成员变量
  • 静态方法不能调用非静态成员,编译会报错(一个非静态可以访问静态成员变量、方法或者非静态属性、方法;然后静态不可以访问非静态成员变量、方法(静态只能访问静态成员变量或者方法))
  • 类和对象都可以调用静态属性;静态变量是随着类的加载而加载,早于对象的创建(由于类只会加载一次.则静态变量在内存中也只会存在一份,存在方法区静态域中)
  • 静态变量如果创建了多个对象,多个对象共享一个静态变量,如果修改其中一个对象的静态变量的话,也会导致其他对象调用那个共享的静态变量是被修改过的
package test;
public class Text {
    
    
    public static void main(String[] args) {
    
    
        System.out.println(Texto.name);//输出结果:张三
        Texto p=new Texto();
        System.out.println(p.name);//输出结果:张三
    }
}
class Texto{
    
    
   static String name="张三";
}
/*
总结:类和对象都可以调用静态属性
*/
package test;
public class Text {
    
    
    public static void main(String[] args) {
    
    
        Texto p=new Texto();
        Texto p1=new Texto();
        System.out.println(p.name);
        System.out.println(p1.name);
        p.name="张三老婆";
        System.out.println(p.name);
        System.out.println(p1.name);
    }
}
class Texto{
    
    
   static String name="张三";
}
/*
输出结果:
张三
张三
张三老婆
张三老婆
*/
//总结:静态属性是所有对象共享的,修改了一个任意对象的值,其他对象都会收到影响

静态方法

访问修饰符 static 返回值类型 方法名(参数列表){
    
    }
  • 静态方法的调用和静态属性差不多
  • 也是随着类的加载而加载,也可以通过类名的方式进行调用
  • 静态方法可以调用静态方法、属性,但不能调用非静态方法、属性;非静态既可以调静态方法、属性也可以调非静态的方法、属性
  • 在静态方法内,不能使用this和super关键字

在这里插入图片描述
图中我们看出静态方法是不能访问或者调用非静态方法或者属性;相反静态方法可以访问静态属性或者非静态属性

总结

  • 静态方便在没有创建对象的情况下进行调用(方法/变量)。

  • 静态修饰成员变量和方法,从属于类;而普通变量和方法从属于对象

  • 由于静态方法不依赖于任何对象就可以直接访问,因此对于静态方法来说,是没有this的,因为不依附于任何对象,既然都没有对象,就谈不上this了,并且由于此特性,在静态方法中不能访问类的非静态成员变量和非静态方法,因为非静态成员变量和非静态方法都必须依赖于具体的对象才能被调用(虽然在静态方法中不能访问非静态成员方法和非静态成员变量,但是在非静态成员方法中是可以访问静态成员方法和静态成员变量。)

  • 如果想在不创建对象的情况下调用某个方法,就可以将这个方法设置为static。最常见的静态方法就是main方法,这就是为什么main方法是静态方法就一目了然了,因为程序在执行main方法的时候没有创建任何对象,只有通过类名来访问。

  • 静态变量被所有对象共享,在内存中只有一个副本,在类初次加载的时候才会初始化,非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响

final关键字

为什么要使用final?为了安全,Java中有一些类,如String,Math等,就是final类的典型例子

final如果修饰类,这个类不能作为父类被继承;如果修饰方法,则这个方法不能被重写;如果修饰常量,则该常量不能重新被赋值(习惯上,常量名用大写字母表示)

final class A{
    
    
}

总结

  • final关键字是最终的意思,可以修饰类、方法、成员变量;被final关键字修饰的方法不能被重写;如果是常量,不能再被赋值;最终类不能被继承

  • final关键字修饰局部变量,变量如果是基本数据类型的话,则数据值不会被改变,如果是引用数据类型,则地址值不会改变(也就是不能再造对象),但地址里面的内容是可以发生改变的

  • final关键字修饰属性,必须显式初始化或者代码块初始化、构造器初始化

  • static final用来修饰属性(通常称为全局常量)、方法

    public static final double PI_VAULE = 3.14;
    
  • final关键字定义的变量名要使用大写字母

abstract关键字

抽象概念

抽象类就是指不能具体实例化的类,也就是不能创建对象。并使用abstract关键字修饰

abstract使用上的注意点

  • 抽象类中可以有成员变量、构造方法以及成员方法
  • 抽象类可以有抽象方法也可以没有抽象方法(抽象方法一定定义在抽象类中)
  • 抽象方法(抽象方法没有方法体,也就是没有这个{})定义在抽象类中(且都必须使用abstract关键字来修饰)可以用来修饰类、方法
//抽象类
public abstract class Animal{
    
    
    //抽象方法
	public abstract void eat();//没有方法体
}
  • 抽象类的意义不在于自身创建对象而在于被继承,当一个类继承抽象类后必须重新抽象类中的抽象方法,否则该类也变成抽象类(如果重写了抽象方法,那么子类可以不是抽象类的话,就可以类的实例化去调用该方法)
public abstract class NumberF {
    
    
    public abstract void eat();
    public void sleep(){
    
    
        System.out.println("睡觉");
    }
}
public (abstract)class Number extends NumberF{
    
    
        @Override
        public void eat() {
    
    
            //子类继承抽象父类,要么方法重写,要么子类是一个抽象类
                System.out.println("1+1!=0");
        }
}
class  NumberText{
    
    
    public static void main(String[] args) {
    
    
        NumberF p=new Number();
        p.eat();
        p.sleep();

    }
}

抽象类使用原则

  • abstract不能用来修饰属性、构造器等结构
  • abstract不能用来修饰私有private方法、静态static方法、final的方法、抽象类的匿名子类
  • 抽象方法必须为public或者protected(因为如果为private,则不能被子类继承,子类便无法实现该方法),缺省情况下默认为public
  • 抽象类必须有子类,使用extends继承,一个子类只能继承一个抽象类

接口(interface关键字)和实现(implements关键字)

先明确一点,接口中的publicstaticfinalabstract有时候可以省略,但省略不代表他没有了访问权限或者是没有了该关键字的作用,接口会默认加上

接口定义

接口是一种引用数据类型,最重要的就是接口中的抽象方法,且接口不能有构造器

interface 接口名称{
    
    
    //接口内容
}

首先不同JDK版本接口包含的内容不一样

  • 但在任何一个版本中,接口都能定义抽象方法
  • 接口当中的抽象方法,修饰符必须是2个固定的关键字:publicabstract(也可以省略不写)
public interface Int {
    
    
    //这是一个抽象方法
    public abstract void eat();
    //这也是抽象方法
    abstract void eat1();
    //这也是抽象方法
    public void eat2();
    //这也是抽象方法
    void eat3();
}

接口使用

接口不能直接使用,必须有一个实现类(implements)去实现该接口

//类实现接口用implements(实现)接口名
public class 实现类名 implements 接口名{
    
    
    
}

接口的实现类必须覆盖重写接口中所有的抽象方法(重写就是去掉abstract关键字,然后加上{}

注意:如果实现类没有覆盖重写接口中所有的抽象方法,那么这个实现类自己就必须是抽象类

public interface Int {
    
    
    public abstract void eat();
}
public class Fu implements Int {
    
    
    @Override
    public void eat() {
    
    
        System.out.println("吃饭");
        //重写就是去掉abstract关键字,然后加上{}
    }
}
public class Text{
    
    
    public static void main(String[] args) {
    
    
        Fu p=new Fu();
        p.eat();
    }
}
//输出结果:吃饭

JDK7

常量(publicstaticfinal)、抽象方法(publicabstract

//举例:
interface InterText{
    
    
	final int MAX=2;
	final int MIN=1;
    //抽象方法
	public abstract void fly();
}

JDK8

常量、抽象方法、默认方法(default)、静态方法

默认方法

//默认方法
public default 返回值类型 方法名称(参数列表){
    
    
    /*
    默认方法必须得有方法体
    在一个接口里可以用default来修饰带有方法体的方法,当别的类实现这个接口时可以不重写带default修饰的方法,也可以重写
    */
}


public interface Int {
    
    
    public abstract void eat();
    public default void sleep(){
    
    
        System.out.println("你真棒");
    }
}
public class Fu implements Int {
    
    
    @Override
    public void eat() {
    
    
        System.out.println("吃饭");
    }
}
public class Text{
    
    
    public static void main(String[] args) {
    
    
        Int p=new Fu();
       p.sleep();
    }
}
//输出结果:你真棒;这里注意几点:在一个接口里可以用default来修饰带有方法体的方法,当别的类实现这个接口时可以不重写带default修饰的方法,也可以重写(调用默认方法,如果实现类当中没有,会向上找接口)

总结

接口的默认方法,可以通过接口实现类对象,直接调用

接口的默认方法,也可以被接口实现类进行覆盖重写

静态方法

//静态方法
public static 返回值类型 方法名称(参数列表){
    
    
    /*
    静态方法必须得有方法体
    接口名称直接调用
    */
}
public interface Int {
    
    
    public abstract void eat();
    public static void sleep(){
    
    
        System.out.println("静态方法");
    }
}
public class Fu implements Int {
    
    
    @Override
    public void eat() {
    
    
        System.out.println("吃饭");
    }
}
public class Text{
    
    
    public static void main(String[] args) {
    
    
       Int.sleep();
    }
}
//输出结果:静态方法
//注意:不能通过接口实现类对象来调用接口当中的静态方法(接口定义的静态方法,只能通过接口去调)

JDK9

常量、抽象方法、默认方法、静态方法、私有方法

私有方法

普通私有方法:解决多个默认方法之间重复的代码问题

静态私有方法:解决多个默认静态方法之间重复的代码问题

//普通私有方法
private 返回值类型 方法名称(){
    
    
    
}
public interface Int {
    
    
    public default void com(){
    
    
        System.out.println("默认方法");
        method();
    }
    public default void comone(){
    
    
        System.out.println("默认方法2");
        method();
    }
    private void method(){
    
    
        System.out.println("你真棒");
        System.out.println("你真棒");
        System.out.println("你真棒");
    }
}

//静态私有方法
private static 返回值类型 方法名称(){
    
    
    
}
public interface Int {
    
    
    public default void com(){
    
    
        System.out.println("静态方法");
        method();
    }
    public default void comone(){
    
    
        System.out.println("静态方法2");
        method();
    }
    private static void method(){
    
    
        System.out.println("你真棒");
        System.out.println("你真棒");
        System.out.println("你真棒");
    }
}
//只能被接口调用,不能被实现类调用

接口中也可以定义成员变量,但必须使用publicstaticfinal三个关键字进行修饰

public static final 数据类型 常量名称=数据值;
public static final int NUM=10;
/*
接口当中的常量,可以省略public、static、final(不写还是默认是全局常量)
接口当中的常量,必须进行赋值,不能不赋值
且常量名字字母必须大写,多个单词得用下划线隔开
*/

使用接口的时候需要注意

  • 接口没有静态代码块或者构造方法的
  • 接口和接口之间可以继承,而且可以多继承
  • 一个类通过关键字implements(实现)声明自己使用一个或者多个接口((接口跟接口之间采用逗号分隔))
//实现多个接口
public interface A {
    
    
   public abstract void eat();
} 
public interface B {
    
    
   public abstract void sleep();
} 
public class C implements A,B{
    
    
    public void eat(){
    
    //实现(重写)
        sout("吃饭");
    }
    public void sleep(){
    
    
        sout("睡觉");
    }
}
  • 如果实现类实现了多个接口当中存在重复定义的抽象方法,那么只需要重写一次就行了(如果实现类没有覆盖所有接口当中的抽象方法,那么实现类就必须是一个抽象类)
public interface Int {
    
    
    void eat();
}
public interface IntTwo {
    
    
    void eat();
}
public class Zi implements Int,IntTwo{
    
    
    @Override
    public void eat() {
    
    
        System.out.println("重复定义只需重写一次即可");
    }
}
public class Text{
    
    
    public static void main(String[] args) {
    
    
        Zi p=new Zi();
        p.eat();
    }
}
//输出结果:重复定义只需重写一次即可
  • 如果实现类实现了多个接口当中有多个重复定义的默认方法,那么实现类一定要对冲突的默认方法进行覆盖重写
interface Int {
    
    
    default void eat(){
    
    
        System.out.println("默认方法1");
    }
}
interface IntTwo {
    
    
    default void eat() {
    
    
        System.out.println("默认方法2");
    }
}
public abstract class Zi implements Int,IntTwo{
    
    
    @Override
    public void eat() {
    
    
        System.out.println("必须重写该默认方法");
    }
}
public class Text{
    
    
    public static void main(String[] args) {
    
    
        Zi p=new Zi();
        p.eat();
    }
}
//输出结果:必须重写该默认方法
  • 如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的方法,那么子类在没有重写此方法的情况下,默认调用的是父类中同名同参数的方法(类优先原则)
interface Int {
    
    
    default void eat(){
    
    
        System.out.println("接口同名");
    }
}
public class Fu {
    
    
    void eat(){
    
    
        System.out.println("父类同名");
    }
}
public class Zi extends Fu implements Int{
    
    
    @Override
    public void eat() {
    
    
        System.out.println("父类优先");
    }
}
public class Text{
    
    
    public static void main(String[] args) {
    
    
        Zi p=new Zi();
        p.eat();
    }
}
//输出结果:父类优先

接口和接口之间是多继承的

public interface IntTwo {
    
    
    void first();
    void method();
}
public interface IntThree {
    
    
    void secondly();
    void method();
}
interface Int extends IntTwo,IntThree{
    
    
    void play();
}
public class Text implements Int{
    
    
    @Override
    public void play() {
    
    
}
    @Override
    public void secondly() {
    
    
}
    @Override
    public void first() {
    
    
}
    @Override
    public void method() {
    
    
}
/*
注意:这个接口有4个方法
多个父接口当中的抽象方法如果冲突,没事;相反多个父接口当中的默认方法如果冲突,那么子接口必须重写默认方法,且必须带default
*/

补充

包装类(封装类)

包装类:把基本数据类型打包成一个number父类
在这里插入图片描述

包装类的使用

  • Java提供8种数据类型对应的包装类,使得基本数据类型的变量具有类的特征

    基本数据类型、包装类、String相互转换

    基本数据类型转换成包装类(调用包装类的构造器)

第一种转换
    Integer i=new Integer(10);
    System.out.println(i);
//输出结果:10
//注意:布尔型只要不是null,不区分大小写

第二种转换
//自动装箱:
    Integer i=10;
    System.out.println(i);

包装类转换成基本数据类型

第一种转换
    Integer p=new Integer(13);
    int i=p.intValue();
    System.out.println(i);
//输出结果:13

第二种转换
//自动拆箱:
    Integer p=10;
    int i=p;
    System.out.println(i);
//输出结果:10

基本数据类型、包装类转换成String类型(调用xxxValue)

第一种转换
    int num=10;
    String str=num+"";
//输出结果:10

第二种转换(记这种)基本数据类型转换成String
    String str=String.valueOf(12.3f);
//输出结果:12.3

Number类转换成String
    Integer p=10;
	String str=String.valueOf(p);
	System.out.println(str);

String类型转换成基本数据类型、包装类(调用包装类的parseXxx())

第一种转换成基本数据类型
	String str="123";
	int i=Integer.parseInt(str);
	System.out.println(i);
//输出结果:123
第二种转换成Number类
    String str="123";
    Integer d=Integer.parseInt(str);
    System.out.println(d);

代码块

代码块(构造代码块),一对括号
{
    
    
    
 	随着对象的创建而执行,每创建一个对象就执行一次构造代码块
	作用:可以在创建对象时,对对象属性等进行初始化。如果多个非静态代码块。按顺序执行
	
}


静态代码块
static{
    
    
    
	静态代码块是随着类的加载而执行(自动执行)而且只执行一次(注意:main方法既是程序入口,也是一个普通的方法,也需要有一个类去调用main方法,调用之前先加载类)
	作用:初始化类的信息。如果有多个静态代码块。按顺序执行
    
}

静态代码块仅执行一次,非静态代码块和构造函数随着对象创建每次都执行,且静态代码块总是在构造函数执行之前执行的
  • 代码块作用:初始化类、对象
  • 代码块如果有修饰的话,只有使用static
  • 静态代码块能有输出语句;不过是随着类的加载而执行一次,初始化类的信息。非静态代码块是随着对象的创建(创建几次执行几次)而执行,可以在创建对象时,对对象的属性进行初始化
  • 静态初始化块,用于类的初始化操作。
  • 静态代码块不能访问普通变量(因为普通变量是被实例对象调用的,静态代码块中不存在其实例对象,所以调用不了)
  • 静态是随着类的加载而加载,非静态是随着对象的加载而加载,静态代码块优先加载,所以静态调用不了非静态属性或者方法(所以静态代码块可以定义在类的任何地方中除了方法体)
package test;
public class Text {
    
    
    public static void main(String[] args) {
    
    
        String p=Box.wife;
        System.out.println(p);
    }
}
class Box{
    
    
    String name="张三";
    static String wife="张三老婆";
    static int age;
 	public Box(){
    
    

  }

    public Box(String name) {
    
    
        this.name = name;
    }

    public  void eat(){
    
    
        System.out.println("张三吃饭");
    }
    public static void sleep(){
    
    
        System.out.println("张三睡觉");
    }
    static {
    
    
        System.out.println("静态");
    }
    {
    
    
        System.out.println("非静态");
    }
}
/*
输出结果:静态
张三老婆
*/

总结

代码块执行顺序静态代码块> 构造代码块 > 构造函数> 普通代码块(由父及子,静态先行)

继承中代码块执行顺序:父类静态块>子类静态块>父类代码块>父类构造器>子类代码块>子类构造器由父及子,静态先行(main方法既是程序入口,也是一个普通的方法,也需要有一个类去调用main方法,调用之前先加载类)

多个类的继承中初始化块、静态初始化块、构造器的执行顺序为:先后执行父类A的静态块,父类B的静态块,最后子类的静态块,然后再执行父类A的非静态块和构造器,然后是B类的非静态块和构造器,最后执行子类的非静态块和构造器

内部类

内部类概念:在一个类中定义了一个类

内部类分为四种:成员内部类(静态和非静态)、局部(方法、代码块、构造器内)内部类、匿名内部类
在这里插入图片描述

//内部类定义格式:

//外部类
public class 类名{
    
    
    //内部类
	class 类名{
    
    
    }
}
//代码块内
{
    
    
    class BB{
    
    
        
    }
}

内部类的特点:内部类可以直接访问外部类的成员,包括私有,而外部内如果想要访问内部类的成员,必须创建对象(在外部类创建方法,然后方法去用私有类的对象去调用去调用私有类的方法)

public class Textone{
    
    
    private int i=10;
	public class Text1{
    
    
        public void show(){
    
    
			System.out.println(i);//输出结果:10;直接访问外部类的成员
        }
    }
    public void method(){
    
    
        Text1 p=new Text1();
        p.show();//外部内如果想要访问内部类的成员,必须创建对象调用
    }
}
public class Text {
    
    
    public static void main(String[] args) {
    
    
        Textone p=new Textone();
        p.method();
    }
}

成员内部类(静态和非静态)

作为一个,内部类可以定义属性、方法、构造器等;可以被final修饰,表示此类不能被继承。相反没有的话可以被继承;可以被abstract修饰,可以被static修饰

作为一个类的成员,调用外部类的结构,可以被static修饰,可以被4种不同的权限修饰

//成员内部类(非静态)
class Person{
    
    
    class ZhangSan{
    
    
        
    }
    public void method(){
    
    
        
    }
}
//成员内部类(静态)
class Person{
    
    
    static class ZhangSan{
    
    
        
    }
    public void method(){
    
    
        
    }
}

如何实例化成员内部类的对象

静态成员内部

class Fu{
    
    
    private String name;
    private int age;
    static class B{
    
    
        public void method(){
    
    
            System.out.println("内部类");
        }
    }
}
public class Text {
    
    
    public static void main(String[] args) {
    
    
          Fu.B p=new Fu.B();//静态成员实例化
    }
}

非静态成员内部

//内部类的定义格式:
外部类名.内部类名 对象名=外部类对象.内部类对象(不是针对私有的)
class Fu{
    
    
    private String name;
    private int age;
    class B{
    
    
        public void method(){
    
    
            System.out.println("内部类");
        }
    }
}
public class Text {
    
    
    public static void main(String[] args) {
    
    
        Fu p=new Fu();
        Fu.B p1=p.new B();
        //等价于
        Fu.B p=new Fu().new B();
        p.method();
    }
}
//输出结果:内部类

成员内部类中区分调用外部类的结构

class Person{
    
    
    String name;
    int age;
    public void eat(){
    
    
        sout("吃饭");
    }
    //静态
	static class Dog{
    
    
		String name;
		int age;
        public void show(){
    
    
			sout("卡拉狗");
        }
    }   
    //非静态
    final class Brid(){
    
    
        String name;
        public Bird(){
    
    
            
        }
        public void sing(){
    
    
            sout("鸟");
            eat();//Person.this.eat();调用外部类的属性
        }
        public void display(String name){
    
    
            sout(name);//方法形参
            sout(this.name);//内部类的属性
            sout(Person.this.name);//外部类的属性 
        }
    }
}
//实例化内部类
public class InnerClassText{
    
    
    public static void main(String[] args){
    
    
        //静态Dog实例(静态成员内部类)
        Person.Dog dog=new Person.Dog();
        dog.show();//输出结果:卡拉狗
        
        //创建Bird实例(非静态成员内部类)
        Person p=new Person();
        Person.Bird bird=p.new Bird();
        bird.sing();//输出结果:鸟 吃饭
    }
}

局部内部类(方法内、代码块内、构造器内)

  • 局部内部类使用的比较少,而且不在定义类的定义域之内便无法使用,其提供的功能使用匿名内部类都可以实现,而本身匿名内部类可以写得比它更简洁,因此局部内部类用的比较少。来看一个局部内部类的小例子就行
  • 由于局部内部类不能在外部类的方法以外的地方使用,因此局部内部类也不能使用控制符合static修饰符修饰。
  • 如果需要用局部内部类定义变量、创建实例或派生子类,那么都只能在局部内部类所在的方法中进行
//方法内
public class InnerClassText{
    
    
    public void method(){
    
    
        class AA{
    
    
            
        }
    }
    public Comparable getComparable(){
    
    
    }
}

匿名内部类

定义:匿名内部类其实就是没有名称的内部类

前提:存在一个接口或者类,这里的类可以是具体类也可以是抽象类

//格式:
new 类名或者接口名(){
    
    
	重写抽象方法;
}

//如果这个是类的话,这个整体相当于子类的实现类;如果是接口的话,这个整体就相当于接口的实现类
new Inter(){
    
    //继承类或者接口
    public void show(){
    
    
        
    }
}

public abstract class Fu{
    
    
    public abstract void eat();
}
public class Text {
    
    
    public static void main(String[] args) {
    
    
        //等价于:Fu父类的子类对象
          new Fu(){
    
    
              @Override
              public void eat() {
    
    
                  System.out.println("匿名内部类");
              }
          };//这里还没有调用,这个整体相当于子类创建好对象
}
//如果要调用:这里有2种形式
   //第一种
new Fu(){
    
    
    @Override
    public void eat() {
    
    
         System.out.println("匿名内部类");
    }.eat();//输出结果:匿名内部类
}
   //第二种
public static void main(String[] args) {
    
    
          Fu p=new Fu(){
    
    //多态的体现形式
              @Override
              public void eat() {
    
    
                  System.out.println("匿名内部类");
              }
          };
          p.eat();
    }//输出结果:匿名内部类
//注意:通过匿名内部类访问局部变量。在JDK8版本之前,必须加上final关键字

接口

interface Int{
    
    
    void again();
}
public class Text {
    
    
    public static void main(String[] args) {
    
    
        new Int(){
    
    
            @Override
            public void again() {
    
    
                System.out.println("重写接口方法");
            }
        }.again();
    }
}
//输出结果:重写接口方法
interface Int{
    
    
    void again();
}
public class Text {
    
    
    public static void main(String[] args) {
    
    
       fun(new Int() {
    
    
           @Override
           public void again() {
    
    
               System.out.println("加油");
           }
       });
    }
    public static void fun(Int i){
    
    //等价于Int i=new Int();
        i.again();
    }
}
//输出结果:加油
//方便调用,如果按往常就得创建一个子类去继承接口,然后通过类的实例化去传对象

猜你喜欢

转载自blog.csdn.net/qq_47256069/article/details/112652683
今日推荐