不能将brassplus类型的值分配到brass类的实体_Java 基础 - 类与对象

 2023-09-07 阅读 27 评论 0

摘要:类、对象和引用的关系类和对象的关系类是对象的模版,对象是类的一个实例,一个类可以有很多对象一个Java程序中类名相同的类只能有一个,也就是类型不会重名一个对象只能根据一个类来创建引用和类以及对象的关系引用只能指向其所属的类型的类的对象相同类型
9c8f9905b2653e95b4eb0e921505f342.png

类、对象和引用的关系

类和对象的关系

  • 类是对象的模版,对象是类的一个实例,一个类可以有很多对象
  • 一个Java程序中类名相同的类只能有一个,也就是类型不会重名
  • 一个对象只能根据一个类来创建

引用和类以及对象的关系

  • 引用只能指向其所属的类型的类的对象
  • 相同类型的引用之间可以赋值
  • 只能通过指向一个对象的引用,来操作一个对象,比如访问某个成员变量

方法

参数传递方式

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*/

分析图:

d119ca8efa527d46fd215562add656ee.png
dcf30cdaf6c76255006f0afd42b65cfb.png

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需要指定的泛型参数、分析图:

eaa8641abdebf5b50e64b59e84f76b60.png
9e1dfba8398762fd4a6dc42bbb8e53e5.png

系统一样赋值了 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:这个理解真的是流弊啊。

子类重写父类方法(覆盖)

方法的重写遵循 “两同两小一大”规则:

  • 方法名相同、形参列表相同
  • 子类方法返回的值类型应比父类方法返回值类型更小或相等,子类方法声明抛出的异常类应该比父类方法声明抛出的异常类更小或相等
  • 子类方法访问权限应该比父类方法的访问权限更大或相等

不能将类型分配到实体?子类中调用父类被覆盖的方法

  • 如果被覆盖的方法是实例方法,使用 super 关键字
  • 如果被覆盖的方法是类方法,使用父类类名
  • 子类不能调用父类中被 private 修饰的方法和属性

子类调用父类构造器

  • 子类不能继承父类的构造器。在子类的构造器中,如果没有显式使用 super 调用父类的构造函数,那么系统一定会在子类构造器执行之前,隐式的调用父类的无参构造器
  • 在子类构造器中,可以使用 super 显式调用父类构造器,但 super 语句必须在第一行

多态

向上类型转换

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 相等的充分非必要条件。

接口

设计思想

  • 接口体现的是规范和实现分离的设计哲学,让软件系统的各组件之间面向接口耦合,是一种松耦合的设计
  • 接口定义的是多个类共同的公共行为规范,这些行为是与外部交流的通道,意味着接口通常是定义一组公共方法

定义

  • 接口的修饰符,只能是 public 或者 default
  • 由于接口定义的是一种规范,所以接口里不能包含构造器和初始化块定义,只能包含静态常量、方法(只能是抽象方法,类方法和默认方法)以及内部类、内部接口、内部枚举
  • 接口里的常量只能是静态常量,默认使用 public static final 修饰
  • 接口里的内部类、内部接口、内部枚举,默认使用 public static 修饰
  • 接口里的抽象方法不能有方法体,但类方法和默认方法必须有方法体。

方法说明

接口中定义抽象方法可以省略 abstract 关键字和修饰符,默认修饰符为 public。

Java 8 新增允许在接口中定义默认方法,使用 default 修饰。默认情况下,系统使用 public 修饰默认方法。

Java 8 新增允许在接口中定义私有方法。

Java 8 新增允许在接口中定义静态方法。静态方法可以被实现的接口的类继承。

使用

一个类可以实现一个或多个接口。

一个类实现一个或多个接口,这个类必须重写所实现的接口中的所有抽象方法。否则,该类必须被定义成抽象类,保留从父接口继承到的抽象方法。

接口不能用来创建实例,但是可以用于声明引用类型的变量,该变量必须指向实现该接口的类的实例对象。

抽象类

抽象类与普通类的区别,可以概括为 “有得有失”。

得,是指抽象类多了一个能力,抽象类可以包含抽象方法

失,是指抽象类失去了一个能力,抽象类不能用于创建实例

抽象类和普通类的区别:

  • 抽象类使用 abstract 修饰
  • 抽象类可以和普通类一样可以包含成员变量、方法、构造器、初始化块、内部类。但抽象类不能被实例化,抽象类的构造器主要用来被子类调用
  • 抽象类可以不包含抽象方法,但是含有抽象方法的类必须被定义为抽象类

抽象类的设计思想:抽象类是模板模式的设计模式体现。抽象类是从多个具体类中抽象出来的父类,具有更高层次的抽象。从多个具有相同特征的类中抽象出一个抽象类,以这个抽象类为其子类的模板,避免子类设计的随意性

内部类

成员内部类

非静态内部类

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 + 

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/4/12365.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息