类、对象和引用的关系
类和对象的关系
引用和类以及对象的关系
方法
参数传递方式
java.lang.clonable是类?Java 总是采用按值调用。
基本类型的值传递
public class PrimitiveTransferTest { public static void swap(int a, int b) { int tmp = a; a = b; b = tmp; System.out.println("swap 方法里 a 的值为: " + a + " b的值为: " + b); } public static void main(String[] args) { int a = 6; int b = 9; swap(a, b); System.out.println("交换结束后 a 的值为 " + a + " b的值为 " + b); }}/**运行结果:swap 方法里 a 的值为: 9 b的值为: 6交换结束后 a 的值为 6 b的值为 9*/
分析图:
java 程序总是从 main() 方法开始执行,main() 方法定义了 a、b 两个局部变量,两个变量在 main 栈区中。在 main() 方法中调用 swap() 方法时,main() 方法此时还未结束,因此系统为 main 方法和 swap 方法分配了两块栈区,用于保存 main 方法和 swap 方法的局部变量。main 方法中的 a、b 变量作为参数传入 swap 方法,实际上是在 swap 方法栈区中重新产生了两个变量 a、b,并将 main 方法栈区中 a、b 变量的值分别赋给 swap 方法栈区中的 a、b 参数(这就是初始化)。此时系统内存中有两个 a 变量、两个 b 变量,只是存在于不同的方法栈区中而已。
引用类型的参数传递
public class ReferenceTransferTest { public static void swap(DataWrap dw) { int tmp = dw.a; dw.a = dw.b; dw.b = tmp; System.out.println("swap 方法里, a 成员变量的的值为: " + dw.a + " b 成员变量的值为: " + dw.b); } public static void main(String[] args) { DataWrap dw = new DataWrap(); dw.a = 6; dw.b = 9; swap(dw); System.out.println("交换结束后, a 成员变量的的值为: " + dw.a + " b 成员变量的值为: " + dw.b); }}/**swap 方法里, a 成员变量的的值为: 9 b 成员变量的值为: 6交换结束后, a 成员变量的的值为: 9 b 成员变量的值为: 6*/
你可能会疑问,dw 对象的成员变量 a、b的值也被替换了,这跟前面基本类型的传递完全不一样。这非常容易让人觉得,调用传入 swap 方法的就是 dw 对象本身,而不是它的复制品。其实传递的依然是 dw 的值。
jparepository需要指定的泛型参数、分析图:
系统一样赋值了 dw 的副本,只是关键在于 dw 只是一个引用变量,它存储的值只是一段内存地址,将该内存地址传递给 swap 栈区,此时 swap 栈区的 dw 和 main 栈区的 dw 的值也就是内存地址相同,该段内存地址指向堆内存中的 DataWrap 对象。对 swap 栈区的 dw 操作,也就是对 DataWrap 对象操作。
重载
重载:同一个类中,方法名相同,参数列表不同。
当调用被重载的方法时,根据参数的个数和类型判断应该调用哪个重载方法,参数完全匹配的方法将被执行。
构造器
Java集合类框架的基本接口有哪些?默认无参构造器
仅当类没有定义任何构造器的时候,系统才会提供一个默认的构造器。这个构造器将所有的实例域设置为默认值。
自定义构造器
当类中有自定义构造器时,系统不会再提供默认的构造器
静态常量
public static final double PI = 3.1415926
静态方法
java中类和方法都不允许嵌套定义、在类加载的时候就存在了,不依赖于任何类的任何实例。
建议通过类名调用,而不是通过实例对象调用,否则很容易混淆概念。
继承
java 使用 extends 作为继承的关键字,有趣的是 extends 是扩展的意思,并不是继承。但是 extends 很好体现了子类和父类的关系,子类是对父类的扩展,子类是一种特殊的父类。扩展更加准确。ps:这个理解真的是流弊啊。
子类重写父类方法(覆盖)
方法的重写遵循 “两同两小一大”规则:
不能将类型分配到实体?子类中调用父类被覆盖的方法
子类调用父类构造器
多态
向上类型转换
Java 引用变量有两个类型。如果编译时类型和运行时类型不一致,就可能出现多态。
示例代码:
public class BaseClass { public int book = 6; public void base() { System.out.println("父类的普通方法"); } public void test() { System.out.println("父类的test方法"); }}public class SubClass extends BaseClass { public String book = "轻量级 Java EE"; public void test() { System.out.println("子类的test方法"); } public void sub() { System.out.println("子类的sub方法"); } public static void main(String[] args) { BaseClass ploymophicBc = new SubClass(); System.out.println(ploymophicBc.book); ploymophicBc.base(); ploymophicBc.test(); // 因为 ploymophicBc 的编译时类型是 BaseClass // BaseClass 类没有提供 sub 方法,所以下面代码编译时会出错 // ploymophicBc.sub(); }}
java的主类必须是public类吗,上面的例子中,引用变量 ploymophicBc 比较特殊,它的编译时类型是 BaseClass,而运行时类型是 SubClass。
ploymophicBc.sub() 这行代码会在编译时报错,因为 ploymophicBc 编译时类型为 BaseClass,而 BaseClass 中没有定义 sub 方法,因此编译时无法通过。
但是注意,ploymophicBc.book 的值为 6, 而不是 ”轻量级 Java EE“。因为对象的实例变量不具备多态性,系统总是试图访问它编译时类型所定义的成员变量,而非运行时。
子类其实是一种特殊的父类,因此 java 允许把父类的引用指向子类对象,这被称为向上转型(upcasting),向上转型由系统自动完成。
可以调用哪些方法,取决于引用类型(编译时)。
具体调用哪个方法,取决于引用指向的实例对象(运行时)。
java中public class和class的区别。向下类型转换
问题:引用变量在代码编译过程中,只能调用它编译时类型具备的方法,而不能调用它运行时类型具备的方法
解决:强制转换成运行时类型
方法:引用类型之间的转换只能在有继承关系的两个类型之间进行,否则编译出错。如果想把一个父类引用变量的编译时类型转换成子类类型,则这个引用变量的运行时类型得是子类类型,否则引发 ClassCastException
示例代码:
//创建子类对象 Dog dog = new Dog(); // 向上类型转换(类型自动提升),不存在风险 Animal animal = dog; // 风险演示 animal 指向 Dog 类型对象,没有办法转化成 Cat 对象,编译阶段不会报错,但是运行会报错Cat cat = (Cat)animal; // 1.编译时按 Cat 类型 2. 运行时 Dog 类型,类型不匹配,直接报错
instanceof
java类默认是public吗。为了解决强制类型转换,可能引发的 ClassCastException 异常,引入 instanceof 运算符。
instanceof 运算符的含义:用于判断左边的对象(运行时类型或者叫实际类型)是否是右边的类或者其子类、实现类的实例。如果是返回 true,否则返回 false。
在之前的代码中,强制类型转换前使用 instanceof 判断:
if (anmial instanceof Cat) { Cat cat = (Cat)animal;}
final 修饰符
final 修饰类
不能被继承
final 修饰方法
不可被子类覆盖
final 修饰变量
特征:变量一旦被初始化,便不可改变
初始化:定义时直接赋值、借助构造函数
对于基本类型域而言,其值是不可变的。
对于引用类型变量而言,它保存的仅仅只是个引用。final 只保证这个变量所引用的地址不会改变,即一直引用同一个对象。但这个对象自身内容完全可以发生改变。
Object 类
toString
toString 用于输出对象的自我描述信息。
Object 类提供的 toString 返回该对象实现类的 "类名 + @ + hashCode"。通常需要重写该方法。
==
对于数值类型的基本变量,只要两个变量的值相等(不需要数据类型完全相同),就返回 true。
对于两个引用类型的变量,只有它们指向同一个对象时,== 判断才会返回 true。
equals
equals 方法是 Object 类提供的一个实例方法。对于引用变量,只有指向同一个对象时才返回 true。一般需要重写 equals 方法。
重写 equals 方法的示例:
public boolean equals(Object obj) { if (this == obj) { return true; } if (obj !=null && obj.getClass() == Person.class) { Person personObj = (Person)obj; if (this.getIdStr().equals(personObj.getIdStr())) { return true; } } return false; }
equals 为 true,hashCode 就应该相等,这是一种约定俗称的规范。即 equals 为 true 是 hashCode 相等的充分非必要条件。
接口
设计思想
定义
方法说明
接口中定义抽象方法可以省略 abstract 关键字和修饰符,默认修饰符为 public。
Java 8 新增允许在接口中定义默认方法,使用 default 修饰。默认情况下,系统使用 public 修饰默认方法。
Java 8 新增允许在接口中定义私有方法。
Java 8 新增允许在接口中定义静态方法。静态方法可以被实现的接口的类继承。
使用
一个类可以实现一个或多个接口。
一个类实现一个或多个接口,这个类必须重写所实现的接口中的所有抽象方法。否则,该类必须被定义成抽象类,保留从父接口继承到的抽象方法。
接口不能用来创建实例,但是可以用于声明引用类型的变量,该变量必须指向实现该接口的类的实例对象。
抽象类
抽象类与普通类的区别,可以概括为 “有得有失”。
得,是指抽象类多了一个能力,抽象类可以包含抽象方法
失,是指抽象类失去了一个能力,抽象类不能用于创建实例
抽象类和普通类的区别:
抽象类的设计思想:抽象类是模板模式的设计模式体现。抽象类是从多个具体类中抽象出来的父类,具有更高层次的抽象。从多个具有相同特征的类中抽象出一个抽象类,以这个抽象类为其子类的模板,避免子类设计的随意性
内部类
成员内部类
非静态内部类
public class Cow { private double weight; public Cow() { } public Cow(double weight) { this.weight = weight; } // 定义一个非静态内部类 private class CowLeg { private double length; private String color; public CowLeg() {} public CowLeg(double length, String color) { this.length = length; this.color = color; } public double getLength() { return this.length; } public void setLength(double length) { this.length = length; } public String getColor() { return this.color; } public void setColor(String color) { this.color = color; } public void info() { System.out.println("当前牛腿的颜色是 " + this.color +
版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。
工作时间:8:00-18:00
客服电话
电子邮件
admin@qq.com
扫码二维码
获取最新动态