向Java程序员的目标前进!
方法的形式参数传递基本数据类型或引用数据类型。形式参数是基本数据类型的话,实际参数就是当前对应的数据值,而且形参的改变不会影响实际参数,比较简单。形式参数是引用数据类型的话,实际参数就是当前对应的空间地址值。
方法的形式参数是引用数据类型时的分析:
如今我们终于可以将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 接口来使用一些设备, * 例如 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的内部类。
按照内部类在类中定义的位置不同,可以分为以下两种格式:
类中能定义内部类,类中能定义方法,方法中能定义内部类,但方法中不能定义方法。
成员内部类中的成员访问外部类的成员变量包括私有
成员内部类中的成员可以访问外部类的成员变量包括私有属性
外部类的成员方法访问成员内部类的方法,创建内部类对象并访问方法即可。格式如下:
外部类名.内部类名 对象名 = new 外部类对象.内部类对象;
外部类访问成员内部类的成员方法,要把外部类的成员内部类当做是外部类的成员,按照格式去创建对象并访问。访问成员方法的格式有静态和非静态之分。
访问非静态方法:
外部类名.内部类名 对象名 = new 外部类名.内部类名();
访问静态方法:可以是上面创建对象的访问方式,或者像下面这样访问:
外部类名.内部类名 对象名 = 外部类名.内部类名.方法名();
一般来说,在实际开发中是不会这样使用的。因为一般内部类就是不让外界直接访问的。
注意事项:
如果成员内部类都是静态的,成员内部类的方法就与静态无关,方法都只能访问静态的外部类成员。
非静态的成员内部类中不能存在静态方法。
/** * 看程序,补全代码,使程序在控制台输出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将其变为常量。
public | protected | 默认 | 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
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)
:比较对象是否相等,引用该类型比较的是地址值是否相同
==:如果连接都是两个基本数据类型:比如在int中比较的是数据值是否相等。如果连接的是引用类型,那么比较的是地址值是否相同
equals()是Object类的方法
equals的源码:
public boolean equals(Object obj) { return (this == obj); }
如果我们不重写Object的equals方法,默认比较的是两个引用类型的地址值是否相同,如果重写了equals方法而且同时重写了hashCode(),则比较的是成员信息是否相同!
注意:无论何时覆盖该方法equals(),通常需要覆盖hashCode方法
博客难免会产生一些错误。如果写的有什么问题,欢迎大家批评指正。