• 中南林业科技大学Java实验报告八:包与接口


    实验8:包与接口

    8.1 实验目的

    • 了解多个目录下,多个类并存且由类同名的情况下对程序运行的影响
    • 掌握Java程序中包的定义以及使用方法
    • 掌握接口定义的语法格式、成员变量的定义、成员方法的定义
    • 掌握接口实现的语法格式
    • 掌握接口的使用和Java语言中利用接口实现多重继承

    8.2 实验内容

    实验指导中的代码直接插到这里来了

    8.2.1 编写两个Java程序,在Tree.java中,显示“我是一棵树”,在Bamboo.java中,显示“我是一棵竹子”。(实验需在命令行中进行编译)

    8.2.1.1 将Tree.java和Bamboo.java放与同一文件夹下

    【前提引入】

    1️⃣ 如果代码中含有中文并且是在windows的cmd命令行进行javac编译指令的执行,如果直接使用javac Tree.java是会报错:不可映射的字符集编码

    1. 这是因为我们的代码文件Tree.java是使用的unicode字符集的UTF-8编码,则存储方式(编码)就为UTF-8。
    2. 但是如果在cmd命令行执行javac编码指令,那么首先是需要读取 Tree.java 文件信息的,但是Windows操作系统默认使用 GBK 字符集,这对程序员就很不友好,导致在对 Tree.java 读取时是使用GBK的解码方式。
    3. Tree.java存储时是 UTF-8 编码,而读取时是 GBK 解码,因此造成了乱码问题。
    4. 但是如果 Tree.java 中不含有中文,我们使用 javac Tree.java 是没有问题的,因为** Unicode字符集和GBK字符集是完全兼容 ASCII字符集的。**

    📍 解决方案:

    1. 在写Tree.java文件时指定字符集为GBK。但是windows11是没有该功能了似乎。
    2. 使用 sublime,notepad 等编译软件,可以指定编码方式为GBK。
    3. 修改windows默认的字符集编码为UTF-8。
    4. 在cmd控制台写命令时指定解码方式为UTF-8,如:javac -encoding utf8 Tree.java。(推荐)

    2️⃣ 对下面的代码显示的结果分析:

    Tree.java 和 Bamboo.java 文件中都含有 Living 类,因此我们如果这样执行:

    1. 编译Tree.java文件:javac -encoding utf8 Tree.java。这样就会在 package-interface文件夹中 生成 Tree.class 和 Living.class 两个字节码二进制文件。
    2. 编译Bamboo.java文件:javac -encoding utf8 Bamboo.java。这样就会生成在 package-interface文件夹中 Bamboo.class 和 Living.class 两个字节码二进制文件。
    3. Living.class命名冲突问题:由于编译 Tree.java 和 Bamboo.java 时都会生成 Living.java 文件,并且会生成在同一级目录package-interface文件夹下,这必然会冲突。那会发生声明呢?即在编译 Bamboo.java 时生成的 Living.class 会发现 package-interface 文件夹下已经有了 Tree.java编译生成的 Living.class,那么就会覆盖掉有 Tree.java编译生成的 Living.class,在 package-interface 文件下就只有由 Bamboo.java 编译生成的 Living.class 字节码二进制文件了。

    【核心代码】

    🌿 Tree.java

    public class Tree
    {
         public static void main(String[] args){
           Living tree=new Living ();
           tree.say();
          }
    }
    class Living
    {
       public void say(){
           System.out.println("我是一棵树");
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    🌿 Bamboo.java

    public class Bamboo
    {
         public static void main(String[] args)
         {
           Living bamboo =new Living ();
           bamboo.say();
          }
    }
    class Living
    {
       public void say()
       {
           System.out.println("我是一棵竹子");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    【运行流程】

    1. 将Tree.java和Bamboo.java放与同一文件夹下。

      image-20221011212027116

    2. 编译Tree.java,运行Tree,观察提示结果。

      image-20221101163723844

    3. 编译Bammboo.java,运行Bammboo,观察提示结果。

      image-20221101162554167

    4. 运行Tree,观察提示结果

      image-20221101164042794

    8.2.1.2 将Tree.java和Bamboo.java放与不同文件夹下

    【前提引入】

    1️⃣ 如果将两个Living类分别放在两个文件夹Tree和Bamboo中,这样在编译这两个 Living.java 的时候由于在不同文件下生成 Living.class 字节码二进制文件,肯定就不会造成命名冲突,也就不会造成覆盖问题。

    📦 谈一谈包

    • 包的三大作用

      1. 目的是区分相同名字的类
      2. 当类很多的时候,能够很好的管理类
      3. 控制访问范围
    • 基础语法

      /* 
      	声明包:package 关键字 打包名称
      	声明当前类所在的包
      */
      package com.bamboo //声明当前类是在com包下的子包bamboo下
          
      /*
      	引用包:import 关键字 打包名称
      	引用某个类
      */
      import java.util.Scanner; //引用到 java包下 的 util包 中的 Scanner类文件
      import java.net.* //引用java包下 的 net包 中的 所有类文件
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
    • 本质

      实际上就是创建不同的 文件夹/目录 来保存类文件

    • 注意事项

      1. package的作用是声明当前类所在的包,需要放在类的最上面,一个类中最多只能有一句package。
      2. import指令位置放在package下面,在类定义上面,可以有多句且没有顺序要求。

    【核心代码】

    • Tree文件夹下的Living.java

      package Tree; //当前在Tree包中
      public class Living
      {
         public void say()
         {
             System.out.println("我是一棵树");
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
    • Bamboo文件夹下的Living.java

      package Bamboo; //声明当前在 Bamboo包下
      public class Living
      {
         public void say()
         {
             System.out.println("我是一个小竹子");
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
    • package-interface文件夹下的Tree.java

      import Tree.Living; //引用在Tree包下的Living类
      public class Tree
      {
           public static void main(String[] args)
           {
             Living tree=new Living ();
             tree.say();
            }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
    • package-interface文件夹下的Bambo.java

      import Bamboo.Living; //找Bamboo包下的Living类
      public class Bamboo
      {
           public static void main(String[] args)
           {
             Living bamboo=new Living ();
             bamboo.say();
            }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9

    【运行流程】

    1. 将两个Living类分别放在两个文件夹Tree和Bamboo中,Tree.java和Bamboo.java放在根目录(文件夹Tree和Bamboo的上一级目录下)

      image-20221101170902471

      image-20221101170951248

      image-20221101170929781

    2. 编译Tree.java和Living.java,运行Tree,观察提示结果。

      #依次执行以下代码
      javac -encoding utf8 Tree\Living.java
      
      javac -encoding utf8 Tree.java
      
      java Tree
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6

      image-20221108134625741

    3. 编译Bamboo.java和Living.java,运行Bamboo,观察提示结果。

      #依次执行以下代码
      javac -encoding utf8 Bamboo\Living.java
      
      javac -encoding utf8 Bamboo.java
      
      java Bamboo
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6

      image-20221108134747170

    4. 再次运行Tree,查看结果。

      image-20221101171419313

    8.2.2 编写一个Java程序,在程序中定义一个接口Bulid,定义一个类Tree实现接口,在Tree类中实现Bulid的接口。

    【前提引入-接口简介】

    • 基本介绍

      接口就是给出一些没有实现的方法,封装到一起,到某个类要使用的时候,再根据具体情况把这些这些方法写出来。

    • 基本语法

      interface 接口名{
      	//属性
          //方法(抽象方法,默认实现方法,静态方法)
      }
      
      • 1
      • 2
      • 3
      • 4
      class 类名 implements 接口名{
          //自己属性
          //自己方法
          
          //必须实现的抽象接口方法
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
    • 注意事项

      1. 接口不能被实例化,必须由类去实现它

      2. 接口所有的方法是 public 方法,接口中抽象方法可以不用 abstract 修饰,因为在javac编译生成 字节码二进制文件 时会认为是抽象方法加上 abstract 关键字。我们在这里可以用 javac反编译指令进行查看:

        image-20221106121013182

      3. 接口中的属性,只能是 final 的,而且必须是 public static final 修饰符,则在定义的时候必须初始化或者使用静态代码块进行初始化。

    • 实现接口 vs 继承类

      1. 接口和继承解决的问题不同:

        • 继承的主要价值:解决diamante复用性和可维护性的问题
        • 接口的主要价值:设计,设计好各种规范(方法),让其它类去实现这些方法。

        实现接口是 对 java单继承机制 的一种很好的补充。

      2. 接口比继承更灵活

        继承是满足 is-a 关系,而接口只需要满足 like-a 关系。

      3. 接口在一定程度上实现 代码解耦(接口规范化+动态绑定机制)

    【核心代码】

    1. Build类

      public interface Build {
          public final static double PI = 3.14;
      
          /**
           * 切面积
           */
          public void area();
      
          /**
           * 体积
           */
          public void volume();
      
          /**
           * 用途
           */
          public void use();
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
    2. Tree类

      public class Tree implements Build {
          /**
           * 树的半径(单位:m)
           */
          private double r;
          /**
           * 树的高度(单位:m)
           */
          private double h;
      
          public Tree(double r, double h) {
              this.r = r;
              this.h = h;
          }
      
          @Override
          public void area() {
              System.out.println("切面积是:" + PI * r * r);
          }
      
          @Override
          public void volume() {
              System.out.println("体积是:" + PI * r * r * h);
          }
      
          @Override
          public void use() {
              System.out.println("我的小树用来造我们的小家");
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
    3. Test类

      public class Test {
          public static void main(String[] args) {
              Tree tree = new Tree(0.5, 5);
              tree.area();
              tree.volume();
              tree.use();
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8

    【运行流程】

    image-20221101172846057

    8.2.3 定义个类Plant,修改(2)声明Tree类继承Plant类,同时实现的Bulid接口内容不变。

    【前提引入】

    继承不多解释,主要谈谈 super 关键字

    • 基本介绍

      super代表对父类(可以不是直接父类,也可以是超类)的引用,用于访问父类的属性、方法、构造器。

    • 基本语法

      1. 可以访问父类的属性:super.属性名,但不能访问父类的 private属性。
      2. 可以访问父类的方法:super.方法名(实参列表),但不能访问父类的 private方法。
      3. 能访问父类的构造器:super(实参列表),完成父类的初始化工作,只能放在构造器的第一句,且只能出现一句。
    • 注意事项:默认情况下构造器中都会隐式存在super(),调用父类的无参构造器。我们举个例子,看下如下代码:

      public class Animal {
          String name;
      
          /**
           * 这是有参构造器,
           * 因此如果没有声明无参构造器,那么该类中不会存在无参构造器
           */
          public Animal(String name) {
              this.name = name;
          }
      }
      
      class Dog extends  Animal{
          public Dog(){
              
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17

      这段代码会是错的,因为我们在调用Dog类的无参构造器中会默认存在一句super(),但是父类 Animal类 中并不存在无参构造器,因此发生错误,修改:

      public class Animal {
          String name;
      
          /**
           * 这是有参构造器,
           * 因此如果没有声明无参构造器,那么该类中不会存在无参构造器
           */
          public Animal(String name) {
              this.name = name;
          }
      }
      
      class Dog extends  Animal{
          public Dog(String name){
              //如果显示的声明了 super调用,那么默认的 super() 就不会存在在代码中了
              super(name);
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18

    【核心代码】

    1. 创建Plant类

      public class Plant {
          private String name;
          private int age;
      
          public Plant(String name, int age) {
              this.name = name;
              this.age = age;
          }
      
          public void introduce() {
              System.out.println("我是一颗生长了 " + age + " 年的 " + name + " 树");
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
    2. 修改Tree类

      public class Tree extends Plant implements Build {
          /**
           * 树的半径(单位:m)
           */
          private double r;
          /**
           * 树的高度(单位:m)
           */
          private double h;
      
          public Tree(double r, double h, String name, int age) {
              //父类构造器初始化
              super(name, age);
              this.r = r;
              this.h = h;
          }
      
          @Override
          public void introduce() {
              //调用父类的 introduce 方法
              super.introduce();
          }
      
          @Override
          public void area() {
              System.out.println("切面积是:" + PI * r * r);
          }
      
          @Override
          public void volume() {
              System.out.println("体积是:" + PI * r * r * h);
          }
      
          @Override
          public void use() {
              System.out.println("我的小树用来造我们的小家");
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
    3. 修改Test类

      public class Test {
          public static void main(String[] args) {
              Tree tree = new Tree(0.5, 5,"逐浪",18);
              tree.introduce();
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6

    【运行流程】

    image-20221101173506058

  • 相关阅读:
    图0000
    编写Android可执行二进制文件调用so
    “灯塔工厂”的中国路径:智造从点到面铺开
    临时记录一下
    k8s-List机制
    java编程基础总结——22. Queue接口及其实现子类
    Postman接口测试工具详解
    P1996 约瑟夫问题
    Netty 之 NioEventLoop 源码阅读
    【代码随想录算法训练营第六十五天|卡码网94.城市间货物运输I&II&III】
  • 原文地址:https://blog.csdn.net/qq_62982856/article/details/127714812