Java的第七篇文章——面向对象接口(包含了接口、static修饰符、final修饰符、main方法、内部类等知识点)

简介: Java的第七篇文章——面向对象接口(包含了接口、static修饰符、final修饰符、main方法、内部类等知识点)

第七章 面向对象接口

学习目标

  • 接口的多继承
  • 实现类实现接口后,还是抽象类
  • 接口的规则体系案例
  • static静态修饰符
  • static内存表现
  • main方法的定义解析
  • 四大权限修饰符
  • final修饰符
  • 内部类
  • 匿名内部类

1.接口的补充

1.1 接口之间的关系

类和类之间是继承关系(单继承)类和接口之间是实现关系(多实现implements),接口和接口之间是继承关系,支持多继承,一个接口可以同时继承多个接口. interface A extends B,C,D{}

1.2 实现类还是抽象类

实现类实现接口,重写一部分抽象方法,然而还有一部分抽象方法还没重写,实现类还是一个抽象类

public interface A {
    public abstract void a1();
    public abstract void a2();
}
public abstract class B implements A {
    public  void a1(){
    }
   // public abstract void a2();
}

抽象类B只是重写了抽象方法a1,还有抽象方法a2没有重写,所以类B还是一个抽象类

1.3 接口规则案例

接口其实就是一种规范,规范了方法的类型和权限,我们只需要按照其规则重写该接口的所有抽象方法,就可以实现接口给我的规范的方法

根据上面的例子,写一个程序

(1)Computer.java文件

/**
 *  笔记本电脑类
 */
public class Computer {
    /**
     *  笔记本电脑,通过USB接口,使用外接设备
     *  方法定义,实现设备的使用
     *  返回值类型 : 这个方法经过运算后的结果的数据类型
     *  参数 : 其实方法的参数,是方法运算过程中的未知数据,才是参数
     *
     *  笔记本电脑,通过USB接口,使用外接设备 这个功能的未知数据,就是外接设备
     *  核心 : 未知设备,都有共同特性 : 满足接口规则
     *
     *  问题 : 这个方法调用 : 传递他什么
     *  参数是引用类,要传递对象,传递接口实现类对象
     */
    public void useUSB(USB usb){
//        USB usb =  new Mouse();
        //接口引用调用方法
        usb.start();
        usb.end();
    }
}

(2)KeyBoadr.java文件

/**
 *  键盘类 : 满足接口标准
 */
public class KeyBoard implements USB{
    @Override
    public void start() {
        System.out.println("键盘工作");
    }
    @Override
    public void end() {
        System.out.println("键盘停止工作");
    }
}

(3)Mouse.java文件

/**
 *  鼠标类 : 接入USB接口工作
 *  满足接口的规则 : 程序中就是实现接口
 */
public class Mouse implements USB{
    @Override
    public void start() {
        System.out.println("鼠标打开开关,按键,滚轮");
    }
    @Override
    public void end() {
        System.out.println("开关关了");
    }
}

(4)接口类USB

/**
 *  定义的USB接口 : 规则指定
 *  接口的规则 : 程序中的抽象方法
 */
public interface USB {
    //设备开始工作
    public abstract void start();
    //设备结束工作
    public abstract void end();
}

(5)测试类USBTest

public class USBTest {
    public static void main(String[] args) {
        //测试笔记本案例
        //创建笔记本对象
        Computer computer = new Computer();
        //调用笔记本的方法 useUSB
       /* Mouse m = new Mouse();
        computer.useUSB(m);*/
        computer.useUSB( new Mouse() );
        //调用笔记本的方法,传递键盘对象 (USB接口实现类对象)
        computer.useUSB( new KeyBoard());
    }
}

computer对象的use()方法只需要按照传进来的USB多态对象,然后去调用start()和end()方法即可,因为有多态的存在,所以computer的use()方法的参数只需要是USB接口类型即可,实现了接口USB的实现类对象均可以当作参数传进来。

2.静态修饰符

static修饰符 : 最早出现在main方法中。只能修饰成员,不能写在方法的内部被static修饰的成员,称为静态成员变量和静态的成员方法。

2.1 静态修饰成员变量

static 修饰的成员变量,是被所有的对象共享的数据。没有被static修饰的成员变量,是每个对象的独享数据或者是特有数据。

(1)Person.java文件

public class Person {
    String name;
    static String country = "中国";
}

(2)StaticTest.java文件

public class StaticTest {
    public static void main(String[] args) {
        Person p1 = new Person();
        p1.name = "张三";
        Person p2 = new Person();
        p2.name = "李四";
        //使用对象p1修改变量country的值
        p1.country = "美国";
        System.out.println(p2.country);
    }
}

2.2 静态内存

  • 静态成员内存的特点
  • 静态成员跟随自己的类进入到元数据区(静态区域)
  • 静态成员属于自己的类,不属于对象
  • 静态成员进入内存后,赋默认值
  • 静态成员变量的初始化实际早于对象

当类被加载时,就会把全部类加载到元数据区,如果成员中有被static修饰的,也一起被加载到元数据区,也就为什么静态成员变量会被共享了。当对象被创建后,没有被static修饰的成员变量会被加载到栈区。

2.3 静态成员的调用方式

静态的内存图中,已经很明白了,静态属于自己的类,不是对象,静态的调用方式应该是类名.静态成员

Person.country ;//调用静态成员
public static void main(String[] args) {
    System.out.println(Person.country);
    Person p1 = new Person();
    p1.name = "张三";
    Person p2 = new Person();
    p2.name = "李四";
    //使用对象p1修改变量country的值
    Person.country = "美国";
    System.out.println(Person.country);
}

(1)静态的调用方式是两种 : 类名调用,对象调用. 非静态成员调用只能对象。

(2)静态成员调用方式只有类名. 非静态成员只能对象. 。

(3)静态成员的对象.静态调用方式,会被javac编译为类名调用。

2.4 静态方法

静态方法直接类名调用。 静态方法中不能直接使用非静态成员.

为什么不能调用非静态成员

可以理解为:静态是先人,非静态是后人,静态出现在内存的时间早于非静态

public class Person {
    String name;
    static String country = "中国";
    /**
     * 静态方法
     */
    public static void eat(){
        System.out.println("人在吃饭" + country);
        System.out.println(name); //错误写法,不允许
    }
}

静态内存优先于对象,在静态的方法中不能使用this和super,理由如下:

(1)this有两种含义,一种是指当前使用的对象,另一种是指当前正在被创建的对象。得先有对象才有this,而静态方法加载到内存中的时间早于对象创建,所以在静态方法中不能使用this关键字。

(2)super关键字是在一个对象内存中,区分父类成员和子类成员的一种标志,所以也是需要当对象被创建后才能使用,所以在静态方法中不能使用super关键字。

2.5 main方法

public static void main(String[] args){}
  • main方法详解
  • public 最大权限 : main方法的调用者是JVM,如果不是public,那么JVM就无法调用main()方法了。
  • static 无需对象,被类名直接调用,JVM启动的时候使用类名.main启动程序。这也就是为什么使用java命令时,不是写类名.class,而是直接写类名,因为java 类名的意思是利用类名.main()的方式去调用main方法。
  • void 无返回值,调用者是JVM,方法的返回值都是返回给调用者,JVM不需要返回值,没有意义。
  • main 固定方法名称
  • args 字符串的数组,JVM调用方法main必须传递参数,后期对JVM设置参数(现阶段可以不需要理解)

2.6 什么时候定义静态

  • 静态成员变量 : 具体功能具体分析 .
  • 当你需要定义一个类的时候,分析这个类new出来的对象,是否存在共享数据,如果有共享数据,应该定义为静态变量
  • 静态成员方法 :
  • 当你类中的方法,没有使用过非静态成员你的时候,应该定义为静态.

3.四大权限

四大权限:public   protected   default   private

权限范围 private default protected public
同一类 OK OK OK OK
同一包 NO OK OK OK
不同包子类 NO NO OK OK
不同包非子类 NO NO NO OK
  • 受保护权限 protected
  • 权限的成员,为子类提供使用
  • Object类是所有类的父类,类中的方法权限有public、protected
  • 关键的方式受保护权限的方法只能是子类中super调用!!!
  • 子类对象不能调用,不出子类

例如:所有的类都继承Object类,该类有一个方法clone()的权限修饰符是protected,调用该方法得用super调用。

如果直接在该方法中创建Object的对象,通过对象再去调用clone()方法是不成立的。

4.final修饰符

final修饰符是最终的意思,不可改变。final可以修饰类,修饰方法,修饰成员变量,修饰局部变量。

4.1 final修饰类

被final修饰的类,称为最终类,不能被其他的类继承,被称为无子类或太监类。

学过的final类有哪些?有String、System、Scanner

public final class A{} //这个类A,不能出现子类,如果继承,直接报错
public class B extends A{} //错误,编译错误,最终类不能继承

4.2 final修饰方法

被final修饰的方法,最终方法,不能被子类重写,和调用无关

一个类中的部分方法很完美,但是另一部分方法有待完成,设计为两个部分。完美的方法就是final

public class A{
    public final void a(){} //方法不能被子类重写
}
public class B extends A{
    public void a(){} //最终方法,不能重写
}

4.3 final修饰局部变量

变量定义在方法的内部,是局部变量,被final修饰后,一次赋值,终身不改变,锁死了变量的值,看做是常量。

  • final修饰的基本类型,锁死值
  • final修饰的引用类型,锁死内存地址 (引用类型中的成员不受影响)
  • final修饰了方法的参数,调用者传递值后,方法的参数值就锁死
public static void main(String[] args) {
    /**
    *   Student student 对象存储的是内存地址
    *   final修饰后,固定住的,不可改变是student变量保存的地址
    *   但是,Student对象中的成员,不受影响
    */
    final  Student student = new Student();
    student.age = 20;
    student.age = 30;
    System.out.println(student.age);
    final int[] arr = {1,2,3};//arr变量的值,固定为内存地址,不可改变
    arr[1] = 200;
    show(5);
    }
    public static void show(final int x){
      x = 6; //final修饰,不可改变,报错
    }
}

4.4 final修饰成员变量

成员变量的定义位置,是在类中,方法外面。成员变量在内存中有默认值。final修饰成员变量的时候,锁住的不是内存默认值,而是我们程序人员手动的赋值。

注意:成员变量如果没有赋值,系统会自动的赋上默认值,而局部变量不会,要我们自己赋值,不然会报错

(1)没加final的成员变量

  • 成员变量赋值,可以定义直接写值 int age = 0;
  • 成员变量赋值,可以使用构造方法 public Student(int age){this.age=age;}
  • 成员变量赋值,可以使用set方法完成

(2)加了final的成员变量

  • 成员变量赋值,可以定义直接写值 int age = 0;
  • final修饰的成员变量,可以构造方法赋值,不能set方法赋值

以下是为什么被final修饰的成员变量不能用set方法赋值的原因:

答:构造方法在new对象的时候,执行一次,仅仅一次。可set方法,反复执行!

public class Student {
    final int age ;
    public Student (int age ){
        this.age = age;
    }
   /*public void setAge(int age){
       this.age = age;
   }这样赋值时错误的*/
}

5.代码块

5.1 静态代码块

写在类中方法外面 : static{}

静态代码块的执行时机:只要使用了这个类的成员(new对象、调用静态方法、静态变量),静态代码块就会执行,而且就一次。

如上图所示,“你真帅!!!”只会执行一次

后面会自己写静态代码块 : 数据库连接池 (C3P0、Druid)

JDBC注册数据库驱动程序,使用静态代码块

5.2 构造代码块

写在类中方法外面的 {}, 创建对象的时候运行,new一次,运行一次(99%不会使用)

5.3 局部代码块

写在方法内部的 {} 局部代码块,没有用(99%不会使用)

6.对象初始化过程(子类和父类)

  • 加载:父类.class文件先进入内存
  • 加载:子类.class文件再进入内存
  • 加载:初始化父类的静态成员(变量,代码块,方法)
  • 加载:初始化子类的静态成员
  • 运行:父类的静态代码块
  • 运行:子类的静态代码块
  • 运行:父类的构造代码块
  • 运行:父类的构造方法
  • 运行:子类的构造代码块
  • 运行:子类的构造方法

记住一点,静态成员比非静态成员先运行。父类的非静态构造比子类的非静态构造先执行!!!

7.内部类

概述 : 所谓内部类,就是在一个类的内部,定义了另外的一个类

class A{ //外部类,封闭类
    class B{} //内部类,嵌套类
}

对象是生活中的存在的事物,一个事物中还存在着另一个具体的事物

class 大楼{
    class 电梯{}
}

7.1 成员内部类

成员内部类,是一个类定义在了另一个类的成员位置这个内部类可以使用成员修饰符,public static final private 。

对于内部来说 : 可以直接使用外部类的成员,如果外部类要使用内部类的成员,必须要创建对象。

公式 : 外部类名.内部类名 变量名 = new 外部类对象().new 内部类对象()

//外部类
public class Outer {
    public void outer(){
        System.out.println("外部类的方法outer");
    }
    //内部类
    public class Inner{
       public void inner(){
           System.out.println("内部类的方法inner");
       }
    }
}
public static void main(String[] args) {
    //调用内部类的方法inner()
    Outer.Inner oi = new Outer().new Inner();
    oi.inner();
}

问题 : 内部类Inner,编译后有class文件吗?答:有class文件,名字是外部类$内部类。

内部类也是类,继承Object,可以实现接口。

7.2 局部内部类

局部内部类 : 要定义在方法里面。方法里面是局部位置,不能使用成员修饰符,权限,静态不能用

class A{
    public void a(){
        class B{} //局部内部类
    }
}
public class Outer {
    /**
     *  Inner类,是方法Outer的局部
     *  依然方法,才能被外界访问
     */
    public void outer(){
        class Inner{
            public void inner(){
                System.out.println("局部内部类的方法!!");
            }
        }
        //方法,建立对象
        //因为是和Inner同级,所以可以直接Inner inner = new Inner()来创建对象
        Inner inner = new Inner();
        inner.inner();
    }
}
public static void main(String[] args) {
    //调用内部类的方法inner()
    //不能直接调用
    Outer outer = new Outer();
    outer.outer();
}

(1)为什么只能这样调用呢?

答:这是因为方法的生命周期问题,当方法结束后,局部内部类也就会笑死,所以不能直接在main()方法中创建局部内部类的对象,只能在方法中创建局部内部类的对象。

(2)为什么局部内部类,访问局部变量,变量必须final修饰?

答:是为了解决两个问题。第一个问题:两个方法使用一个变量,自己修改自己的,不能保证最终结果是否为我们想要的。第二个问题:生命周期问题,对象的生命长于方法,out方法已经出栈消失,Inner类的对象还活着,对象.in()内存的生命问题,in方法在访问变量x的时候,变量在内存中消失了。Sun公司为了解决两个问题,那就别改变量了,加上final得了。局部内部类,使用局部变量,变量必须final修饰,jdk8会默认添加,jdk7必须自己手动写入,不然编译过不去。

注意:final是个障眼法,在编译后,final修饰的变量会变成一个常数。所以被final修饰的变量的值不会消失,能与局部内部类共处。

用idea反编译后,就可以发现被final修饰的变量消失了,被final修饰的变量会变成一个常数5,这就避免了变量的生命周期的问题。

7.3 匿名内部类

匿名内部类,就是没有名字的内部类,只能写在方法中,为了简化代码书写。

简化:实现类实现接口,重写方法,创建对象。或者是子类继承父类,重写方法,创建对象。代码上少内容。

  • 匿名内部类使用的前提:
  • 必须有接口实现,或者是类的继承
  • 格式 :实现类,实现接口,重写方法,创建对象
new 接口或者父类(){
    //重写抽象方法
};
public interface MyInter {
    public abstract void inter();
    public abstract void inter2();
}

(1)这是非匿名的创建方式

public class InnerClassTest {
    public static void main(String[] args) {
        //匿名内部类,简化书写,不写实现类
        //同时调用多个重写方法
        /*
         *  new MyInter(){}; 是接口实现类的匿名对象
         * 多态 : 接口 变量 = 实现类对象
         */
       MyInter my =  new MyInter(){
            @Override
            public void inter() {
                System.out.println("实现类实现接口重写方法");
            }
            @Override
            public void inter2() {
                System.out.println("实现类实现接口重写方法2222");
            }
        };
       my.inter();
       my.inter2();
    }
}

(2)这是匿名的创建方式

public class InnerClassTest {
    public static void main(String[] args) {
        new MyInter(){
            @Override
            public void inter() {
                System.out.println("你好美!!!");
            }
            @Override
            public void inter2() {
                System.out.println("你好帅!!!");
            }
        }.inter();
    }
}

两种方式的主要区别就是匿名的只能使用一次,而且要注意后面的分号,因为匿名调用是要加方法的,所以的加上分号,告知计算机你到这里就结束了。

8.非法修饰符组合

非法的修饰符的组合,主要说的是抽象abstract

  • abstract和private就是非法组合,抽象方法要重写,private不能继承。
  • abstract和final就是非法组合,抽象方法要重写,final修饰不能重写。
  • 被static修饰的方法没有重写这么一说。

我已经重写了Person中类的方法,而在多态调用时,eat()方法却没和student对象实现动态绑定,这是因为在编译时,person.eat()会被编译器改为Person.eat(),也就是通过类名来调用静态方法。

感谢尚硅谷老师的指导!!!

相关文章
|
11天前
|
JSON Java Apache
非常实用的Http应用框架,杜绝Java Http 接口对接繁琐编程
UniHttp 是一个声明式的 HTTP 接口对接框架,帮助开发者快速对接第三方 HTTP 接口。通过 @HttpApi 注解定义接口,使用 @GetHttpInterface 和 @PostHttpInterface 等注解配置请求方法和参数。支持自定义代理逻辑、全局请求参数、错误处理和连接池配置,提高代码的内聚性和可读性。
|
2天前
|
Java
在Java中,接口之间可以继承吗?
接口继承是一种重要的机制,它允许一个接口从另一个或多个接口继承方法和常量。
18 1
|
12天前
|
Java
java线程接口
Thread的构造方法创建对象的时候传入了Runnable接口的对象 ,Runnable接口对象重写run方法相当于指定线程任务,创建线程的时候绑定了该线程对象要干的任务。 Runnable的对象称之为:线程任务对象 不是线程对象 必须要交给Thread线程对象。 通过Thread的构造方法, 就可以把任务对象Runnable,绑定到Thread对象中, 将来执行start方法,就会自动执行Runable实现类对象中的run里面的内容。
29 1
|
15天前
|
存储 Java 程序员
Java基础的灵魂——Object类方法详解(社招面试不踩坑)
本文介绍了Java中`Object`类的几个重要方法,包括`toString`、`equals`、`hashCode`、`finalize`、`clone`、`getClass`、`notify`和`wait`。这些方法是面试中的常考点,掌握它们有助于理解Java对象的行为和实现多线程编程。作者通过具体示例和应用场景,详细解析了每个方法的作用和重写技巧,帮助读者更好地应对面试和技术开发。
55 4
|
18天前
|
Java 开发者
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
在Java多线程编程的世界里,Lock接口正逐渐成为高手们的首选,取代了传统的synchronized关键字
44 4
|
20天前
|
Java 测试技术 Maven
Java一分钟之-PowerMock:静态方法与私有方法测试
通过本文的详细介绍,您可以使用PowerMock轻松地测试Java代码中的静态方法和私有方法。PowerMock通过扩展Mockito,提供了强大的功能,帮助开发者在复杂的测试场景中保持高效和准确的单元测试。希望本文对您的Java单元测试有所帮助。
49 2
|
21天前
|
Java 网络安全 Maven
Exception in thread "main" java.lang.NoSuchMethodError: okhttp3.OkHttpClient$Builder.sslSocketFactory(Ljavax/net/ssl/SSLSocketFactory;Ljavax/net/ssl/X509TrustManager;)Lokhttp3/OkHttpClient$Builder; 问题处理
【10月更文挑战第26天】Exception in thread "main" java.lang.NoSuchMethodError: okhttp3.OkHttpClient$Builder.sslSocketFactory(Ljavax/net/ssl/SSLSocketFactory;Ljavax/net/ssl/X509TrustManager;)Lokhttp3/OkHttpClient$Builder; 问题处理
37 2
|
JavaScript 前端开发 Java
|
6天前
|
Java 开发者
Java多线程编程中的常见误区与最佳实践####
本文深入剖析了Java多线程编程中开发者常遇到的几个典型误区,如对`start()`与`run()`方法的混淆使用、忽视线程安全问题、错误处理未同步的共享变量等,并针对这些问题提出了具体的解决方案和最佳实践。通过实例代码对比,直观展示了正确与错误的实现方式,旨在帮助读者构建更加健壮、高效的多线程应用程序。 ####
|
5天前
|
安全 Java 开发者
Java 多线程并发控制:深入理解与实战应用
《Java多线程并发控制:深入理解与实战应用》一书详细解析了Java多线程编程的核心概念、并发控制技术及其实战技巧,适合Java开发者深入学习和实践参考。