Java教程

Java学习之路之week4day2

本文主要是介绍Java学习之路之week4day2,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

向Java程序员的目标前进!

day16

面向对象—续6

学习内容

  • day16
  • 面向对象—续6
    • 接口—续
      • 形式参数问题
      • 练习:接口作为形式参数的猫狗案例
      • 返回值问题
      • 接口方式和继承方式的实现效果比较
      • 练习:USB接口
    • 内部类
      • 内部类的位置
      • 成员内部类的使用
        • 成员内部类和外部类的访问方式
        • 成员内部类常见的修饰符
      • 面试题:内部类和外部类没有继承关系
      • 局部内部类的使用
      • 面试题:局部内部类访问局部变量的条件
      • 权限修饰符
      • 其它修饰符的使用
      • 包及代码分层的含义
        • 代码中包的分类
    • Object类的两个常用方法
      • 常用方法
      • ==和equals的区别

接口—续

形式参数问题

方法的形式参数传递基本数据类型或引用数据类型。形式参数是基本数据类型的话,实际参数就是当前对应的数据值,而且形参的改变不会影响实际参数,比较简单。形式参数是引用数据类型的话,实际参数就是当前对应的空间地址值。

方法的形式参数是引用数据类型时的分析:

  • (如果引用数据类型是)具体类:实际参数应该创建当前具体类对象
  • 抽象类:实际参数需要传递应该创建当前抽象类的子类对象(抽象类多态)
  • 接口:先定义接口的子实现类,然后实际参数中传递子实现类的匿名对象。(接口多态)
  • 数组:实际参数需要传递数组对象

如今我们终于可以将week1的那张数据类型图补充完整了

在这里插入图片描述

练习:接口作为形式参数的猫狗案例

/**
 * 设计一个Java程序
 * (1)定义一个接口CanCry,描述会吼叫的方法public void cry()
 * (2)分别定义狗类(Dog)和猫类(Cat),实现CanCry接口。实现方法的功能分别为:
 *  打印输出“我是狗,我的叫声是汪汪汪”、“我是猫,我的叫声是喵喵喵”
 * (3)定义一个主类G,
 * 	定义一个void  makeCry(CanCry c)方法,其中让会吼叫的事物吼叫。
 * 	在main方法中创建狗类对象(dog)、猫类对象(cat)、G类对象(g),用
 * 	g调用makecry方法,让狗和猫吼叫。
 */

//定义一个接口CanCry,描述会吼叫的方法public void cry()
interface CanCry {
    public void cry() ;
}

//猫类
class Cat implements CanCry {
    @Override
    public void cry() {
        System.out.println("我是猫,我的叫声是喵喵喵");
    }
}

//狗类
class Dog implements CanCry {
    @Override
    public void cry() {
        System.out.println("我是狗,我的叫声是汪汪汪");
    }
}

//测试类
class G {
    public static void main(String[] args) {
        //创建G类对象
        G g = new G() ;
        //方式1:创建具体的对象名:接口多态
        //实际参数:需要传递接口 子实现类对象
        CanCry canCry = new Dog() ;
        g.makeCry(canCry);
        canCry = new Cat() ;
        g.makeCry(canCry) ;
        System.out.println("------------------------") ;
        //方式2:匿名对象
        g.makeCry(new Dog()) ;
        g.makeCry(new Cat());
    }
    //成员方法
    public void makeCry(CanCry c){
        c.cry();
    }
}

返回值问题

方法的返回值要么是基本数据类型,要么是引用数据类型。方法的返回值如果是基本数据类型,那么方法声明中是什么类型,使用对应的类型接收。这是前面学习方法时介绍的。后期我们学习的都是引用数据类型。

方法的返回值是引用数据类型时的分析:

  • (如果方法的返回值是)具体类:需要返回的是当前类的具体对象!
  • 抽象类:需要返回的是当前抽象类的子类对象(也可以用匿名对象方式返回——抽象类多态)
  • 接口:需要返回的是接口的子实现类对象(接口多态)

接口方式和继承方式的实现效果比较

我们对这两个方式分别举例说明,最后总结。

如果我们写了一个具体类,里面实现了很多功能(方法)。那么我们再定义一个类,想重写上面的部分方法时,就需要用到继承。那么用到继承就产生了方法重写,方法重写会将所有的公共访问方法都继承过来,这样就会导致有些方法我们即使不需要,但也被带了过来。

换成面向接口编程的方式开发会怎么样呢?如果我们写了一个接口并定义了一些抽象方法,那么新定义一个类去实现接口,就要实现接口中所有的方法。如果还要重写方法,我们再写一个接口,把我们想重写的方法放进去,新定义类时只要重写选择的方法就可以了。

我们不妨以生活中的例子描述一下。类就相当于买菜的时候去批发,要么全都要,要么都不要;接口相当于零售,你要一个或者多个或者全要都可以。

综上所述,接口的实现效果比继承的实现效果更灵活,更有优势。面向接口编程是我们以后开发的主要形式。

练习:USB接口

/**
 * 需求说明:
 *      在现实生活中,我们经常通过电脑的 USB 接口来使用一些设备,
 *      例如 mp3 、移动硬盘、优盘等。现在要求使用面向接口编程去模拟实现这个例子。
 *      实现步骤
 *      (1)创建 USB 接口,接口中只定义一个 work()方法。
 *      (2)创建 MP3 类并实现 USB 接口。
 *      (3)创建优盘类(U_Disk)并实现 USB 接口。
 *      (4)创建电脑类(Computer)并定义一个使用接口的方法 useMobile(USB u)。
 *      (5)测试类中分别创建对应的对象进行测试,MP3对象,优盘类对象,电脑对象)
 */

//电脑类
class Computer {
    //电脑就是本身使用移动设备,查入不同的设备,实现不同的功能!
    //将构造方法私有化
    private Computer(){}
    //提供静态功能
    public static void useMobile(USB u){ //测试的时候:方法的形式参数是一个接口
        u.work() ;
    }
}

//MP3类
class MP3 implements USB {
    @Override
    public void work() {
        System.out.println("可以听音乐了...");
    }
}

//U盘类
class U_Disk implements USB {
    @Override
    public void work() {
        System.out.println("可以拷贝文件了");
    }
}

//USB接口
interface USB {
    void work() ;
}

//测试类
public class Test {
    public static void main(String[] args) {
        //使用Compupter的功能
        USB usb  = new MP3() ; //接口多态
        Computer.useMobile(usb);

        usb = new U_Disk() ; //接口多态
        Computer.useMobile(usb) ;
        
        System.out.println("---------------------------------") ;

        Computer.useMobile(new MP3());
        Computer.useMobile(new U_Disk());
    }
}

内部类

把类定义在其他类的内部,这个类就被称为内部类。举个例子,一个类A中定义一个类B,那么类B就称为类A的内部类。

内部类的位置

按照内部类在类中定义的位置不同,可以分为以下两种格式:

  1. 成员内部类:在外部类的成员位置定义的类
  2. 局部内部类:在外部类的成员方法中定义的类

类中能定义内部类,类中能定义方法,方法中能定义内部类,但方法中不能定义方法。

成员内部类中的成员访问外部类的成员变量包括私有

成员内部类的使用

成员内部类和外部类的访问方式

成员内部类中的成员可以访问外部类的成员变量包括私有属性

外部类的成员方法访问成员内部类的方法,创建内部类对象并访问方法即可。格式如下:

	外部类名.内部类名 对象名 = new 外部类对象.内部类对象;

外部类访问成员内部类的成员方法,要把外部类的成员内部类当做是外部类的成员,按照格式去创建对象并访问。访问成员方法的格式有静态和非静态之分。

访问非静态方法:

	外部类名.内部类名 对象名 = new 外部类名.内部类名();

访问静态方法:可以是上面创建对象的访问方式,或者像下面这样访问:

	外部类名.内部类名 对象名 = 外部类名.内部类名.方法名();

一般来说,在实际开发中是不会这样使用的。因为一般内部类就是不让外界直接访问的。

成员内部类常见的修饰符

  • private:可以保证数据的安全性
  • static:使数据访问更方便

注意事项:

  • 如果成员内部类都是静态的,成员内部类的方法就与静态无关,方法都只能访问静态的外部类成员。

  • 非静态的成员内部类中不能存在静态方法。

面试题:内部类和外部类没有继承关系

/**
 * 看程序,补全代码,使程序在控制台输出30,20,10
 */

class Outer {
    public int num = 10; 
    class Inner { 
        public int num = 20; 
        public void show() { 
            int num = 30; 
            //补全这3处代码
            System.out.println(); 
            System.out.println(); 
            System.out.println(); 
        } 
    } 
}

class OuterDemo { 
    public static void main(String[] args) { 
        Outer.Inner oi = new Outer().new Inner(); 
        oi.show(); 
    } 
}

答案:

num
this.num
Outer.this.num 或者 new Outer().num

局部内部类的使用

局部内部类可以直接访问外部类的成员。可以创建内部类对象,通过对象调用内部类方法,来使用局部内部类的功能。

局部内部类特点:局部内部类也依然访问外部类的成员变量,包括私有

面试题:局部内部类访问局部变量的条件

问题:局部内部类访问局部变量的时候,能访问吗?局部变量有什么要求?

解答:

jdk7及jdk7之前,局部变量必须显示加入final修饰,否则访问会报错。而jdk8已经将jvm优化了,此时的num2是个常量!

局部变量的生命周期是随着的方法的调用而存在,随着方法的调用结束而消失。当前这个方法结束之后,num2局部变量应该就不存在了,但是我们还在使用内部类对象访问它里面的成员方法。对象不会立即被垃圾回收器立即回收,而是在空闲时回收掉没有更多引用的对象。所以此时这些变量都是常驻内存,应使用final将其变为常量。

权限修饰符

publicprotected默认private
同一类中
同一包的子类,其他类
不同包子类
不同包其它类

其它修饰符的使用

  • static:结合自定义常量使用,可结合final使用

    	public static final 数据类型 变量名 = 初始化数据;
    

    static还可以在工具类中使用,工具类中的构造方法私有化,里面的方法都会加入static,使用类名访问。

  • abstract:一般是修饰类,当作抽象类来使用。还可以在成员方法中定义,比如在抽象类中定义:

    	public abstract 返回值类型 方法名(参数列表) ;
    

    abstract在接口中定义的抽象方法中abstract可以省略

包及代码分层的含义

包就是文件夹(目录)。我们在开发项目的时候,不可能将所有的java文件都放在一个包下。所以真实的开发环境中包是需要针对代码分层的。

代码中包的分类

com.xxx.pojo/entity/domain:存储的是符合”JavaBean规范“的、描述现实世界事物的实体类。这个包下会将属性私有化,并对外提供公共的访问方法setter and getter。它(在分布式项目中)可以实现序列化接口(serializable接口)。

com.xxx.service:业务接口层,也叫服务层,是用来实现功能的。

com.xxx.dao:数据库访问层,也叫持久层,它是数据访问的接口层,通过JDBC连接数据库。

com.xxx.controller:控制层。用来调用业务层数据方法,利用前后端的交互技术,进行数据的视图的渲染!

Object类的两个常用方法

每个类都有Object作为超类(父类),所有的类都默认继承自Object

常用方法

public final Class getClass():获取当前正在运行的Class类对象(字节码文件对象)

在Class类中,public String getName()用于获取当前类或者接口的名称,以字符串形式返回

public int hashCode():理解 “一个地址值”,不是实际意义的地址值,它是经过底层的哈希算法(hash)算出来的

hashcode的源码:

	public native int hashCode();

native修饰的方法是本地方法,由非Java语言实现。底层是c相关的语言

public String toString():返回对象的字符串表示形式,结果应该是一个简明扼要的表达,容易让人阅读。 建议所有子类覆盖此方法。

toString的源码:

	public String toString() {
        //包名.类名 + @ + 十六进制数据
    	return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

重写toString方法时使用开发环境提供的”模板“

public boolean equals(Object obj):比较对象是否相等,引用该类型比较的是地址值是否相同

==和equals的区别

==:如果连接都是两个基本数据类型:比如在int中比较的是数据值是否相等。如果连接的是引用类型,那么比较的是地址值是否相同

equals()是Object类的方法

equals的源码:

	public boolean equals(Object obj) {
    	return (this == obj);
	}

如果我们不重写Object的equals方法,默认比较的是两个引用类型的地址值是否相同,如果重写了equals方法而且同时重写了hashCode(),则比较的是成员信息是否相同!

注意:无论何时覆盖该方法equals(),通常需要覆盖hashCode方法

博客难免会产生一些错误。如果写的有什么问题,欢迎大家批评指正。

这篇关于Java学习之路之week4day2的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!