- 参考资料
- 1. 面向对象特征之二:继承性
- 1.1 继承的概念
- 1.2 继承的作用
- 1.3 继承的类型
- 1.4 继承的特性
- 1.5 继承关键字
- 1.6 练习
- 2. 方法的重写
- 2.1 定义
- 2.2 要求
- 2.3 示例
- 2.4 方法的重载回顾
- 2.5 重写与重载之间的区别
- 3. 四种访问权限修饰符
- 4. 关键字:super
- 4.1 作用
- 4.2 调用父类的构造器
- 4.3 this和super的区别
- 5. 子类对象实例化过程
- 6. 面向对象特征之三:多态性
- 6.1 理解多态性
- 6.2 多态的优点
- 6.3 多态存在的三个必要条件
- 6.4 对象类型转换
- 6.5 虚拟方法/虚函数调用(Virtual Method Invocation)
- 6.6 练习
- 7. Object类的使用
- 7.1 主要结构
- 7.2 ==操作符与equals方法
- 7.3 toString的使用
- 8. 包装类(Wrapper)的使用
- 8.1 基本类型、包装类和String之间的转换
- 8.2 包装类用法举例
- 9. 练习
- 10. 扩展:native关键字
- https://www.bilibili.com/video/BV1Kb411W75N
- https://www.runoob.com/java/java-inheritance.html
- 继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
- 为什么要有继承?
多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,
那么多个类无需再定义这些属性和行为,只要继承那个类即可。
此处的多个类称为子类(派生类),单独的这个类称为父类(基类或超类)。 - 类继承语法规则:
class Subclass extends SuperClass{ }
- 继承的出现减少了代码冗余,提高了代码的复用性。
- 继承的出现,更有利于功能的扩展。
- 继承的出现让类与类之间产生了关系,提供了多态的前提
- 注意:不要仅为了获取其他类中某个功能而去继承
- 子类继承了父类,就继承了父类的方法和属性。
- 在子类中,可以使用父类中定义的方法和属性,也可以创建新的数据和方法。
- 在Java 中,继承的关键字用的是extends,即子类不是父类的子集,而是对父类的扩展
- 关于继承的规则
- java只支持单继承和多层(重)继承,不允许多继承。
一个子类只能有一个父类
一个父类可以派生出多个子类
- 单继承与多层继承举例
-
子类拥有父类非 private 的属性、方法。
-
子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
-
子类可以用自己的方式实现父类的方法。
-
Java 的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如 B 类继承 A 类,C 类继承 B 类,所以按照关系就是 B 类是 C 类的父类,A 类是 B 类的父类,这是 Java 继承区别于 C++ 继承的一个特性。
-
提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系越紧密,代码独立性越差)。
- 继承可以使用 extends 和 implements 这两个关键字来实现继承(implements与接口有关),而且所有的类都直接或间接地继承于 java.lang.Object类;意味着,所有的 java 类具有 java.lang.Object类声明的功能。,当一个类没有继承的两个关键字,则默认继承object(这个类在 java.lang 包中,所以不需要 import)祖先类。
class ManKind {
private int sex;
private int salary;
public void setSalary(int salary) {
this.salary = salary;
}
public int getSalary() {
return salary;
}
public void setSex(int sex) {
this.sex = sex;
}
public int getSex() {
return sex;
}
public void manOrWoman() {
if(sex==1)System.out.println("man");
if(sex==0)System.out.println("woman");
}
public void employeed() {
if(this.salary==0)System.out.println("no job");
if(this.salary!=0)System.out.println("job");
}
}
class Kids extends ManKind{
private int yearsOld;
public void setYearsOld(int yearsOld) {
this.yearsOld = yearsOld;
}
public int getYearsOld() {
return yearsOld;
}
public void printAge() {
System.out.println("year old: "+this.yearsOld);
}
}
public class KidsTest{
public static void main(String[] args) {
Kids someKid = new Kids();
someKid.setYearsOld(25);
someKid.printAge();
// someKid.sex;
someKid.manOrWoman();
someKid.setSalary(100000);
someKid.getSalary();
someKid.employeed();
}
}
2. 方法的重写
2.1 定义
在子类中可以根据需要对从父类中继承来的方法进行改造,也称为方法的重置、覆盖。在程序执行时,子类的方法将覆盖父类的方法。
2.2 要求 方法的声明:权限修饰符 返回值类型 方法名(形参列表) throws 异常的类型{
//方法体
}
- 子类重写的方法必须和父类被重写的方法具有相同的方法名称、参数列表
- 子类重写的方法的返回值类型不能大于父类被重写的方法的返回值类型
返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类(java5 及更早版本返回类型要一样,java7 及更高版本可以不同)。 - 子类重写的方法使用的访问权限不能小于父类被重写的方法的访问权限
- 例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected。
- 子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和final 的方法。
- 子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public和protected 的非 final 方法。
- 子类方法抛出的异常不能大于父类被重写方法的异常
重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。 - 构造方法不能被重写。
- 子类与父类中同名同参数的方法必须同时声明为非static的(即为重写),或者同时声明为
static的(不是重写)。因为static方法是属于类的,子类无法覆盖父类的方法。
2.3 示例在方法前面加上@Override 系统可以帮你检查方法的正确性。所以,加上Override标签后如果你写错重写的方法,系统会给你检验。
class Animal {
public void move() {
System.out.println("anamal can move");
}
}
class Dog extends Animal {
@Override
public void move() {
System.out.println("dog can run");
}
}
public class TestDog {
public static void main(String args[]) {
Animal a = new Animal();
Animal b = new Dog();
a.move();
b.move();
}
}
输出为
java TestDog anamal can move dog can run
class Animal {
public void move() {
System.out.println("anamal can move");
}
}
class Dog extends Animal {
public void move() {
System.out.println("dog can run");
}
public void bark() {
System.out.println("dog can shout");
}
}
public class TestDog {
public static void main(String args[]) {
Animal a = new Animal();
Animal b = new Dog();
a.move();
b.move();
b.bark();
}
}
输出:
该程序将抛出一个编译错误,因为b的引用类型Animal没有bark方法。2.4 方法的重载回顾
-
重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。
-
每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。
-
最常用的地方就是构造器的重载。
-
重载规则
- 被重载的方法必须改变参数列表(参数个数或类型不一样);
- 被重载的方法可以改变返回类型;
- 被重载的方法可以改变访问修饰符;
- 被重载的方法可以声明新的或更广的检查异常;
- 方法能够在同一个类中或者在一个子类中被重载。
- 无法以返回值类型作为重载函数的区分标准。
- 方法的重写(Overriding)和重载(Overloading)是java多态性的不同表现,重写是父类与子类之间多态性的一种表现,重载可以理解成多态的具体表现形式。
(1) 方法重载是一个类中定义了多个方法名相同,而他们的参数的数量不同或数量相同而类型和次序不同,则称为方法的重载(Overloading)。
(2) 方法重写是在子类存在方法与父类的方法的名字相同,而且参数的个数与类型一样,返回值也一样的方法,就称为重写(Overriding)。
(3) 方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现。
- 对于class的权限修饰只可以用public和default(缺省)。
- public类可以在任意地方被访问。
- default类只可以被同一个包内部的类访问。
-
在Java类中使用super来调用父类中的指定操作:
- super可用于访问父类中定义的属性
- super可用于调用父类中定义的成员方法
- super可用于在子类构造器中调用父类的构造器
-
注意点
- 尤其当子父类出现同名成员时,可以用super表明调用的是父类中的成员
- super的追溯不仅限于直接父类
- super和this的用法相像,this代表本类对象的引用,super代表父类的内存
空间的标识
-
示例
class Person {
protected String name = "张三";
protected int age;
public String getInfo() {
return "Name: " + name + "nage: " + age;
}
}
class Student extends Person {
protected String name = "李四";
private String school = "New Oriental";
public String getSchool() {
return school;
}
public String getInfo() {
return super.getInfo() + "nschool: " + school;
}}
public class StudentTest {
public static void main(String[] args) {
Student st = new Student();
System.out.println(st.getInfo());
}}
4.2 调用父类的构造器
- 子类中所有的构造器默认都会访问父类中空参数的构造器
- 当父类中没有空参数的构造器时,子类的构造器必须通过this(参数列表)或者super(参数列表)语句指定调用本类或者父类中相应的构造器。同时,只能”二选一”,且必须放在构造器的首行
- 如果子类构造器中既未显式调用父类或本类的构造器,且父类中又没有无参的构造器,则编译出错
-举例
public class Person {
private String name;
private int age;
private Date birthDate;
public Person(String name, int age, Date d) {
this.name = name;
this.age = age;
this.birthDate = d;
}
public Person(String name, int age) {
this(name, age, null);
}
public Person(String name, Date d) {
this(name, 30, d);
}
public Person(String name) {
this(name, 30);
}
}
public class Student extends Person {
private String school;
public Student(String name, int age, String s) {
super(name, age);
school = s;
}
public Student(String name, String s) {
super(name);
school = s;
}
// 编译出错: no super(),系统将调用父类空参数的构造器但是父类中没有空参数的构造器
public Student(String s) {
school = s;
}
}
4.3 this和super的区别
5. 子类对象实例化过程
-
从结果上看:
- 子类继承父类以后,就获取了父类中声明的属性或方法。
- 创建子类的对象中,在堆空间中,就会加载所有父类中声明的属性。
-
从过程上看:
当我们通过子类的构造器创建子类对象时,我们一定会直接或间接的调用其父类构造器; 直到调用了java.lang.Object类中空参的构造器为止。正因为加载过所有的父类结构,所以才可以看到内存中有父类中的结构,子类对象可以考虑进行调用。 -
明确:虽然创建子类对象时,调用了父类的构造器,但自始至终就创建过一个对象,即为new的子类对象。
class Creature {
public Creature() {
System.out.println("Creature无参数的构造器");
}}
class Animal extends Creature {
public Animal(String name) {
System.out.println("Animal带一个参数的构造器,该动物的name为" + name);
}
public Animal(String name, int age) {
this(name);
System.out.println("Animal带两个参数的构造器,其age为" + age);
}}
public class Wolf extends Animal {
public Wolf() {
super("灰太狼", 3);
System.out.println("Wolf无参数的构造器");
}
public static void main(String[] args) {
new Wolf();
}}
输出为:
Creature无参数的构造器 Animal带一个参数的构造器,该动物的name为灰太狼 Animal带两个参数的构造器,其age为3 Wolf无参数的构造器6. 面向对象特征之三:多态性 6.1 理解多态性
-
多态性,是面向对象中最重要的概念,在Java中的体现:
对象的多态性:父类的引用指向子类的对象
可以直接应用在抽象类和接口上 -
多态是同一个行为具有多个不同表现形式或形态的能力。
-
多态就是同一个接口,使用不同的实例而执行不同操作。
-
Java引用变量有两个类型:编译时类型和运行时类型。编译时类型由声明
该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。简
称:编译时,看左边;运行时,看右边。- 若编译时类型和运行时类型不一致,就出现了对象的多态性(Polymorphism)
- 多态情况下,“看左边”:看的是父类的引用(父类中不具备子类特有的方法);“看右边”:看的是子类的对象(实际运行的是子类重写父类的方法)
-
对象的多态 —在Java中,子类的对象可以替代父类的对象使用
- 一个变量只能有一种确定的数据类型
- 一个引用类型变量可能指向(引用)多种不同类型的对象
Person p = new Student(); Object o = new Person();//Object类型的变量o,指向Person类型的对象 o = new Student(); //Object类型的变量o,指向Student类型的对象
-
子类可看做是特殊的父类,所以父类类型的引用可以指向子类的对象:向
上转型(upcasting)。 -
一个引用类型变量如果声明为父类的类型,但实际引用的是子类对象,那么该变量就不能再访问子类中添加的属性和方法
Student m = new Student(); m.school = “pku”; //合法,Student类有school成员变量 Person e = new Student(); e.school = “pku”; //非法,Person类没有school成员变量 //属性是在编译时确定的,编译时e为Person类型,没有school成员变量,因而编译错误。6.2 多态的优点
- 消除类型之间的耦合关系
- 可替换性
- 可扩充性
- 接口性
- 灵活性
- 简化性
- 继承
- 重写
- 父类引用指向子类对象,如:Parent p = new Child();
对象的多态性:只适用于方法,不适用于属性(编译和运行都看左边)
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。
如何才能调用子类所特有的属性和方法?
答: 使用强制类型转换符,也可称为:向下转型
-
instanceof 操作符
-
x instanceof A:检验x是否为类A的对象,返回值为boolean型。
-
使用情境:为了避免在向下转型时出现ClassCastException异常,我们在进行向下转型之前,先进行 instanceof的判断,一旦返回true,就进行向下转型。如果返回false,不进行向下转型。
-
如果a instanceof A返回true,则a instanceof B也返回true。 其中类B是类A的父类。
-
-
示例
public class Test {
public static void main(String[] args) {
show(new Cat()); // 以 Cat 对象调用 show 方法
show(new Dog()); // 以 Dog 对象调用 show 方法
Animal a = new Cat(); // 向上转型
a.eat(); // 调用的是 Cat 的 eat
Cat c = (Cat)a; // 向下转型
c.work(); // 调用的是 Cat 的 work
}
public static void show(Animal a) {
a.eat();
// 类型判断
if (a instanceof Cat) { // 猫做的事情
Cat c = (Cat)a;
c.work();
} else if (a instanceof Dog) { // 狗做的事情
Dog c = (Dog)a;
c.work();
}
}
}
abstract class Animal {
abstract void eat();
}
class Cat extends Animal {
public void eat() {
System.out.println("吃鱼");
}
public void work() {
System.out.println("抓老鼠");
}
}
class Dog extends Animal {
public void eat() {
System.out.println("吃骨头");
}
public void work() {
System.out.println("看家");
}
}
输出为
吃鱼 抓老鼠 吃骨头 看家 吃鱼 抓老鼠6.5 虚拟方法/虚函数调用(Virtual Method Invocation)
- 子类中定义了与父类同名同参数的方法,在多态情况下,将此时父类的方法称为虚拟方法,父类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译期是无法确定的。
- Java 中其实没有虚函数的概念,它的普通函数就相当于 C++ 的虚函数,动态绑定是Java的默认行为。如果 Java 中不希望某个函数具有虚函数特性,可以加上 final 关键字变成非虚函数。
- 动态绑定(晚绑定)
对于多态,只有等到方法调用的那一刻,解释运行器才会确定所要调用的具体方法。 - 静态绑定(早绑定)
重载,是指允许存在多个同名方法,而这些方法的参数不同。编译器根据方法不0
同的参数表,对同名方法的名称做修饰。对于编译器而言,这些同名方法就成了不同的方法。它们的调用地址在编译期就绑定了。Java的重载是可以包括父类和子类的,即子类可以重载父类的同名不同参数的方法。所以:对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法。
6.6 练习面试题:多态是编译时行为还是运行时行为?
答:运行时行为
继承成员变量和继承方法的区别
- 若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的同名方法,系统将不可能把父类里的方法转移到子类中。
- 对于实例变量则不存在这样的现象,即使子类里定义了与父类完全相同的实例变量,这个实例变量依然不可能覆盖父类中定义的实例变量
public class FieldMethodTest {
public static void main(String[] args){
Sub s= new Sub();
System.out.println(s.count); //20
s.display();//20
base b = s;
//==:对于引用数据类型来讲,比较的是两个引用数据类型变量的地址值是否一样。
System.out.println(b == s); //true
System.out.println(b.count); //10
b.display();
}
}
class base {
int count= 10;
public void display() {
System.out.println(this.count);
}
}
class Sub extends base {
int count= 20;
public void display() {
System.out.println(this.count);
}
}
package pers.chh3213.classLearn20211228;
public class InstanceTest{
public static void main(String[] args) {
InstanceTest test = new InstanceTest();
test.method(new Graduate());
}
public void method(Person e) {
String inforString = e.getInfo();
System.out.println(inforString);
if (e instanceof Graduate){
System.out.println("a graduate student");
System.out.println("a student");
System.out.println("a person");
// Graduate new_name = (Graduate) e;
}
else if (e instanceof Student) {
System.out.println("a student");
System.out.println("a person");
}
else if (e instanceof Person) {
System.out.println("a person");
}
}
}
class Person {
protected String name = "person";
protected int age=50;
public String getInfo() {
return "Name:"+name+"n"+"age:"+age;
}
}
class Student extends Person{
protected String school="pku";
public String getInfo() {
return "Name:"+name+"n"+"age:"+age+"nschool:"+school;
}
}
class Graduate extends Student{
public String major ="IT";
public String getInfo() {
return "Name:"+name+"n"+"age:"+age+"nschool:"+school+"nmajor:"+major;
}
}
- GeometricObject类
package pers.chh3213.classLearn20211228;
public class GeometricObject {
protected String color;
protected double weight;
protected GeometricObject(String color,double weight) {
// TODO Auto-generated constructor stub
this.color = color;
this.weight = weight;
}
public String getColor() {
return color;
}
public double getWeight() {
return weight;
}
public void setColor(String color) {
this.color = color;
}
public void setWeight(double weight) {
this.weight = weight;
}
public double findArea() {
return 0.0;
}
}
- Circle类
package pers.chh3213.classLearn20211228;
public class Circle extends GeometricObject {
private double radius;
public Circle(double radius,String color,double weight) {
// TODO Auto-generated constructor stub
super(color, weight);
this.radius=radius;
}
public void setRadius(double radius) {
this.radius = radius;
}
public double getRadius() {
return radius;
}
public double findArea() {
return Math.PI*radius*radius;
}
}
- MyRectangle类
package pers.chh3213.classLearn20211228;
public class MyRectangle extends GeometricObject{
private double width;
private double height;
public MyRectangle(double width,double height,String color,double weight) {
// TODO Auto-generated constructor stub
super(color, weight);
this.width=width;
this.height=height;
}
public void setHeight(double height) {
this.height = height;
}
public void setWidth(double width) {
this.width = width;
}
public double getHeight() {
return height;
}
public double getWidth() {
return width;
}
public double findArea() {
return this.width*this.height;
}
}
- GeometricTest类
package pers.chh3213.classLearn20211228;
public class GeometricTest {
public static void main(String[] args) {
GeometricTest test = new GeometricTest();
test.displayGeometricObject(new MyRectangle(10, 20, null, 0));
test.displayGeometricObject(new Circle(5, null, 0));
test.equalsArea(new MyRectangle(10, 20, null, 0), new MyRectangle(10, 10, null, 0));;
}
public void equalsArea(GeometricObject ob1,GeometricObject ob2) {
if(ob1.findArea()==ob2.findArea())System.out.println("Areas are equal");
else {
System.out.println("Areas are not equal");
}
}
public void displayGeometricObject(GeometricObject objecet) {
System.out.println("area is:"+objecet.findArea());
}
}
面试题:多态是编译时行为还是运行时行为?
答:运行时行为,证明见如下:
import java.util.Random;
class Animal {
protected void eat() {
System.out.println("animal eat food");
}
}
class Cat extends Animal {
protected void eat() {
System.out.println("cat eat fish");
}
}
class Dog extends Animal {
public void eat() {
System.out.println("Dog eat bone");
}
}
class Sheep extends Animal {
public void eat() {
System.out.println("Sheep eat grass");
}
}
public class InterviewTest {
public static Animal getInstance(int key) {
switch (key) {
case 0:
return new Cat ();
case 1:
return new Dog ();
default:
return new Sheep ();
}
}
public static void main(String[] args) {
int key = new Random().nextInt(3);
System.out.println(key);
Animal animal = getInstance(key);
animal.eat();
}
}
- 扩展:仔细思考这段代码
package pers.chh3213.classLearn20211228;
//考查多态的笔试题目:
public class InterviewTest1 {
public static void main(String[] args) {
base base = new Sub();
base.add(1, 2, 3);
// Sub s = (Sub)base;
// s.add(1,2,3);
}
}
class base {
public void add(int a, int... arr) {
System.out.println("base");
}
}
class Sub extends base {
public void add(int a, int[] arr) {
System.out.println("sub_1");
}
// public void add(int a, int b, int c) {
// System.out.println("sub_2");
// }
}
输出为
sub_17. Object类的使用
- Object类是所有java类的根父类
- 如果在类的声明中未使用extends关键字指明其父类,则默认父类为java.lang.Object类
-
==
- 基本类型比较值:只要两个变量的值相等,即为true.
int a=5; if(a==6)… - 引用类型比较引用(是否指向同一个对象):只有指向同一个对象时,==オ返回true.
Person p1=new Person(); Person p2=new Person(); if (p1==p2) ...)
- 用"=="进行比较时,符号两边的数据类型必须兼容(可自动转换的基本
数据类型除外),否则编译出错
- 基本类型比较值:只要两个变量的值相等,即为true.
-
equals():所有类都继承了Object,也就获得了equals()方法。还可以重写。
- 只能比较引用类型,其作用与“==”相同,比较是否指向同一个对象。
- 格式:obj1.equals(obj2)
特例:当用equals()方法进行比较时,对类File, String、 Date及包装类
(Wrapper Class)来说,是比较类型及内容而不考虑引用的是否是同一个对
象;
原因:在这些类中重写了object类的equals()方法。
当自定义使用equals()时,可以重写。用于比较两个对象的“内容”是否都
相等
-
重写equals()方法的原则
- 对称性:如果x.equals(y)返回是"true" ,那么y.equals(x)也应该返回是
“true” - 自反性: x.equals(x)必须返回是"true"
- 传递性:如果x.equals(y)返回是"true" ,而且y.equals(z)返回是"true
那么z.equals(x)也应该返回是"true" - 一致性:如果x.equals(y)返回是"true" ,只要x和y内容一直不变,不管你
重复x.equals(y)多少次,返回都是"true" - 任何情况下, x.equals(null),永远返回是"false" ;x.equals(和x不同类型的对象)永远返回是"false" 。
- 对称性:如果x.equals(y)返回是"true" ,那么y.equals(x)也应该返回是
-
面试题: ==和equals的区别
- ==既可以比较基本类型也可以比较引用类型。对于基本类型就是比较值,对于引用类型就是比较内存地址
- equals的话,它是属于java.lang.Object类里面的方法,如果该方法没有被重写过默认也是==;我们可以看到String等类的equals方法是被重写过的,而且String类在日常开发中
用的比较多,久而久之,形成了equals是比较值的错误观点。 - 具体要看自定义类里有没有重写Object的equals方法来判断。
- 通常情况下,重写equals方法,会比较类中的相应属性是否都相等。
面试题
package pers.chh3213.classLearn20211228;
public class MainTest {
public static void main(String[] args) {
test();
}
public static void test() {
char[] arr = new char[] {'a','b','c'};
System.out.println(arr);//输出为数组值:abc
int[] arr1 = new int[] {1,2,3};
System.out.println(arr1);//输出为地址值:[I@515f550a
double[] arr2 = new double[] {1.1,2.2,3.3};
System.out.println(arr2);//输出为地址值:[D@626b2d4a
}
}
8. 包装类(Wrapper)的使用
- 针对八种基本数据类型定义相应的引用类型一包装类(封装类)
- 有了类的特点,就可以调用类中的方法, Java才是真正的面向对象
- 基本数据类型包装成包装类的实例–(装箱)
通过包装类的构造器实现:
int i = 500; Integer t = new Integer(i);
还可以通过字符串参数构造包装类对象:
Float f = new Float("4.56");
Long I = new Long("asdf); //NumberFormatException - 获得包装类对象中包装的基本类型变量–拆箱
调用包装类的.xxxValue()方法:
boolean b = bObj.booleanValue(); - JDK1.5之后,支持自动装箱, 自动拆箱。但类型必须匹配。
- 字符串转换成基本数据类型
通过包装类的构造器实现:
int i = new Integer("12");
通过包装类的parsexxx(String s)静态方法:
Float f = Float.parseFloat("12.1"); - 基本数据类型转换成字符串
调用字符串重载的valueOf(方法:
String fstr = String.valueOf(2.34);
更直接的方式:
String intStr = 5+ "n
8.2 包装类用法举例通俗地说:将对象转换为基本数据类型就是拆箱。将基本数据类型转换为对象的过程就是装箱。
面试题:如下两个题目输出结果相同吗?各是什么?
Object o1= true? new Integer(1) : new Double(2.0); System.out.println(o1);//返回1.0 Object o2; if(true) o2 = new Integer(1); else o2 = new Double(2.0); System.out.println(o2);//返回1
面试题:以下输出结果?
public static void method1() {
Integer i = new Integer(1);
Integer j = new Integer(1);
// System.out.println(i);
System.out.println(i == j); //比较的是对象地址:false
//Integer内部定义了一个IntegerCache结构,IntegerCache中定义Integer[]
//保存了从-128-127范围的整数。如果我们使用自动装箱的方式,给Integer赋值的范围在其中时,
//可以直接使用数组中的元素,不用再去new了。目的,提高效率。
Integer m = 1;
Integer n = 1;
// System.out.println(m);
System.out.println(m == n);//true
Integer x = 128;//相当于new了一个Integer对象
Integer y = 128;//相当于new了一个Integer对象
System.out.println(x == y);//false
}
练习
package pers.chh3213.classLearn20211228;
import java.util.Vector;
import java.util.Scanner;
public class VectorTest {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
Vector vector =new Vector();
int max = 0;
System.out.println("请输入学生成绩(以负数代表输入结束)");
while (true) {
int score =scanner.nextInt();
if(score<0)break;
else if(score>100){
System.out.println("输入的数据>100,请重新输入");
continue;
}
vector.addElement(score);
if(score>max)max=score;
}
//遍历数组
char level;
for (int i = 0; i < vector.size(); i++) {
Object object = vector.elementAt(i);
int score=(int)object;
if(max-score<=10)level='A';
else if (max-score<=20) {
level='B';
}else if (max-score<=30) {
level='C';
}else {
level='D';
}
System.out.println("student-" + i + " score is " + score + ",level is " + level);
}
System.out.println();
}
}
9. 练习
- 写一个名为 Account 的类模拟账户。该类的属性和方法如下图所示。该类包括的属性:
账号 id,余额 balance,年利率 annualInterestRate;包含的方法:访问器方法(getter 和setter 方法),返回月利率的方法 getMonthlyInterest(),取款方法 withdraw(),存款方法deposit()。
写一个用户程序测试 Account 类。在用户程序中,创建一个账号为 1122、余额为 20000、年利率 4.5%的 Account 对象。使用 withdraw 方法提款 30000 元,并打印余额。再使用 withdraw 方法提款 2500 元,使用 deposit 方法存款 3000 元,然后打印余额和月利率。
提示: 在提款方法 withdraw 中,需要判断用户余额是否能够满足提款数额的要求,如果不能,应给出提示。
运行结果如图所示:
- Account 类
package pers.chh3213.classLearn20211229;
public class Account {
private int id;
private double balance;
private double annualInterestRate;
public Account(int id,double balance,double annualInterestRate) {
// TODO Auto-generated constructor stub
this.id =id;
this.balance=balance;
this.annualInterestRate=annualInterestRate;
}
public double getAnnualInterestRate() {
return annualInterestRate;
}
public double getBalance() {
return balance;
}
public int getId() {
return id;
}
public void setAnnualInterestRate(double annualInterestRate) {
this.annualInterestRate = annualInterestRate;
}
public void setBalance(double balance) {
this.balance = balance;
}
public void setId(int id) {
this.id = id;
}
public double getMonthlyInterest() {
return this.annualInterestRate/12;
}
public void withdraw(double amount) {
//取款
if(this.balance-amount>=0)this.balance=this.balance-amount;
else {
System.out.println("余额不足!");
}
}
public void deposit(double amount) {
//存钱
this.balance=this.balance+amount;
}
}
- AccountTest类
package pers.chh3213.classLearn20211229;
public class AccountTest {
public static void main(String[] args) {
Account account = new Account(1122, 20000, 4.5);
account.withdraw(30000);
System.out.println("您的账户余额为: "+account.getBalance());
account.withdraw(3000);
System.out.println("您的账户余额为: "+account.getBalance()+"n"+"月利率为:"+account.getMonthlyInterest()+"%");
}
}
- 创建 Account 类的一个子类 CheckAccount 代表可透支的账户,该账户中定义一个属性overdraft 代表可透支限额。在 CheckAccount 类中重写 withdraw 方法,其算法如下:
如果(取款金额<账户余额),可直接取款
如果(取款金额>账户余额),计算需要透支的额度,判断可透支额 overdraft 是否足够支付本次透支需要,如果可以将账户余额修改为 0,冲减可透支金额,如果不可以,提示用户超过可透支额的限额
要求:写一个用户程序测试 CheckAccount 类。在用户程序中,创建一个账号为 1122、余额为 20000、年利率 4.5%,可透支限额为 5000 元的 CheckAccount 对象。 使用 withdraw 方法提款 5000 元,并打印账户余额和可透支额。 再使用 withdraw 方法提款 18000 元,并打印账户余额和可透支额。 再使用 withdraw 方法提款 3000 元,并打印账户余额和可透支额。 提示: (1) 子类 CheckAccount 的构造方法需要将从父类继承的 3 个属性和子类自己的属性全部初始化。 (2) 父类Account的属性balance被设置为private,但在子类CheckAccount的withdraw方法中需要修改它的值,因此应修改父类的 balance 属性,定义其为 protected。
- CheckAccount子类
package pers.chh3213.classLearn20211229;
public class CheckAccount extends Account {
private double overdraft;
public static void main(String[] args) {
CheckAccount checkAccount = new CheckAccount(1122, 20000, 4.5, 5000);
checkAccount.withdraw (5000);
System.out.println("您的账户余额为:"+checkAccount.getBalance());
System.out.println("您的可透支额为:"+checkAccount.getOverdraft());
checkAccount.withdraw (18000);
System.out.println("您的账户余额为:"+checkAccount.getBalance());
System.out.println("您的可透支额为:"+checkAccount.getOverdraft());
checkAccount.withdraw (3000);
System.out.println("您的账户余额为:"+checkAccount.getBalance());
System.out.println("您的可透支额为:"+checkAccount.getOverdraft());
}
public CheckAccount(int id,double balance,double annualInterestRate,double overdraft) {
super(id, balance, annualInterestRate);
this.overdraft = overdraft;
}
public void setOverdraft(double overdraft) {
this.overdraft = overdraft;
}
public double getOverdraft() {
return overdraft;
}
@Override
public void withdraw(double amount) {
// TODO Auto-generated method stub
if (amountsuper.getBalance()) {
double overdraft = amount-super.getBalance();
if(this.overdraft>=overdraft) {
super.setBalance(0);
this.overdraft-=overdraft;
}
else {
System.out.println("超过可透支额的限额!!!");
}
}
}
}
- 考察equals()
if(username.equals(“admin”){
....
}
答案:
if(“admin”.equals(username)){
}
- int 和 Integer 有什么区别
答:
1.Java 提供两种不同的类型:引用类型和原始类型(或内置类型)。int是java的原始数据类型,Integer是java为int提供的封装类。Java为每个原始类型提供了封装类。
2.原始类型封装类型及其对应的包装类:boolean Boolean,char Character,byte Byte,short Short,int Integer,long Long,float Float,doubl Double
3.引用类型和原始类型的行为完全不同,并且它们具有不同的语义。
4.引用类型和原始类型具有不同的特征和用法,它们包括:大小和速度问题,这种类型以哪种类型的数据结构存储,当引用类型和原始类型用作某个类的实例数据时所指定的缺省值。
5.对象引用实例变量的缺省值为 null,而原始类型实例变量的缺省值与它们的类型有关。
- 以下代码的运行结果:
public static void main(String[] args) {
Integer i1 = 128;
Integer i2 = 128;
int i3 = 128;
int i4 = 128;
System.out.println(i1 == i2);
System.out.println(i3 == i4);
System.out.println(i1 == i3);
}
答案: false true true Integer的i1和i2是对象,他们==比较的是地址。 如果-128~127范围,那么使用缓存的常量对象,如果超过这个范围,是新new的对象,不是常量对象10. 扩展:native关键字
- 使用 native 关键字说明这个方法是原生函数,也就是这个方法是用 C/C++等非Java 语言实现的,并且被编译成了 DLL,由 java 去调用。
- 为什么要用 native 方法
java 使用起来非常方便,然而有些层次的任务用 java 实现起来不容易,或者我们对程序的效率很在意时,问题就来了。例如: 有时 java 应用需要与 java 外面的环境交互。这是本地方法存在的主要原因,你可以想想 java 需要与一些底层系统如操作系统或某些硬件交换信息时的情况。本地方法正是这样一种交流机制:它为我们提供了一个非常简洁的接口,而且我们无需去了解 java 应用之外的繁琐的细节。 - native 声明的方法,对于调用者,可以当做和其他 Java 方法一样使用
1.一个 native method 方法可以返回任何 java 类型,包括非基本类型,而且同样可以进行异常控制。
2.native method 的存在并不会对其他类调用这些本地方法产生任何影响,实际上调用这些方法的其他类甚至不知道它所调用的是一个本地方法。 JVM 将控制调用本地方法的所有细节。
3.如果一个含有本地方法的类被继承,子类会继承这个本地方法并且可以用 java语言重写这个方法(如果需要话)。



