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

时间:2022-11-08 08:55:22

实验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("我是一棵树");
   }
}

???? 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. 将Tree.java和Bamboo.java放与同一文件夹下。

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

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

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

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

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

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

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

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

【核心代码】

  • Tree文件夹下的Living.java

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

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

    import Tree.Living; //引用在Tree包下的Living类
    public class Tree
    {
         public static void main(String[] args)
         {
           Living tree=new Living ();
           tree.say();
          }
    }
    
  • 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. 将两个Living类分别放在两个文件夹Tree和Bamboo中,Tree.java和Bamboo.java放在根目录(文件夹Tree和Bamboo的上一级目录下)

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

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

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

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

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

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

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

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

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

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

【前提引入-接口简介】

  • 基本介绍

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

  • 基本语法

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

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

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

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

    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();
    }
    
  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("我的小树用来造我们的小家");
        }
    }
    
  3. Test类

    public class Test {
        public static void main(String[] args) {
            Tree tree = new Tree(0.5, 5);
            tree.area();
            tree.volume();
            tree.use();
        }
    }
    

【运行流程】

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

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(){
            
        }
    }
    

    这段代码会是错的,因为我们在调用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. 创建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 + " 树");
        }
    }
    
  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("我的小树用来造我们的小家");
        }
    }
    
  3. 修改Test类

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

【运行流程】

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