1.面向对象特征之继承性
继承的原因:当多个类中存在相同属性或行为时,将相同内容抽取到一个单独类中,多个类只需继承这个类即可。多个类称为子类|派生类,单独的类称为父类|基类|超类。
①extends关键字:只能实现继承一个类;②implement关键字:实现接口用的。一个类可同时实现多个接口,相当于具有多继承特性。
作用:减少代码冗余,提高代码复用性;有利于功能的扩展;让类与类之间产生关系,让多态的使用提供基础和前提。不要仅为了获取其他类中某个功能而去继承。
特征:子类继承父类,就继承父类所有方法和属性;子类可调用父类的方法和属性,也可创建新的方法和属性,实现功能的拓展。子类不是父类子集,且两者关系不同于子集和集合的关系。缺点:提高类之间的耦合性。
父类中声明为private的属性或方法,子类继承父类后,仍然认为获取了父类的私有结构。因为封装性的影响,使得子类不能直接调用父类的结构。
class 父类{
}
// 单继承
class 子类 extends 父类 {
}
// 多重继承
class 子类的子类 extends 子类 {
}
1.1.练习继承性
写一个用户程序测试 Account 类。在用户程序中,创建一个账号为 1122、余额为 20000、年利率 4.5%的 Account 对象。使用 withdraw 方法提款 30000 元,并打印余额。再使用 withdraw 方法提款 2500 元,使用 deposit 方法存款 3000 元,然后打印余额和月利率。
public class Account {
private int id;// 账号
private double balance;// 余额
private double annualInterestRate;// 年利率
public Account(int id, double balance, double annualInterestRate) {
super();
this.id = id;
this.balance = balance;
this.annualInterestRate = annualInterestRate;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
public double getAnnualInterestRate() {
return annualInterestRate;
}
public void setAnnualInterestRate(double annualInterestRate) {
this.annualInterestRate = annualInterestRate;
}
// 月利率
public double getMonthlyInterest() {
return annualInterestRate / 12;
}
// 取钱
public void withdraw(double amount) {
if (balance >= amount) {
balance -= amount;
return;
}
System.out.println("余额不足");
}
// 存钱
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}
}
// 测试
public class AccountTest {
public static void main(String[] args) {
Account account = new Account(1122, 20000, 0.045);
account.withdraw(30000);
System.out.println("您的账户余额为:" + account.getBalance());
account.withdraw(2500);
System.out.println("您的账户余额为:" + account.getBalance());
account.deposit(3000);
System.out.println("您的账户余额为:" + account.getBalance());
System.out.println("月利率 = " + account.getMonthlyInterest());
}
}
// 输出
余额不足
您的账户余额为:20000.0
您的账户余额为:17500.0
您的账户余额为:20500.0
月利率 = 0.00375
创建 Account 类的一个子类 CheckAccount 代表可透支的账户,该账户中定义一个属性overdraft 代表可透支限额。在 CheckAccount 类中重写 withdraw 方法,其算法如下:如果(取款金额<账户余额),可直接取款;如果(取款金额>账户余额),计算需要透支的额度,判断可透支额 overdraft 是否足够支付本次透支需要,如果可以将账户余额修改为 0,冲减可透支金额如果不可以提示用户超过可透支额的限额。
public class CheckAccount extends Account {
private double overdraft;// 可透支限额
public double getOverdraft() {
return overdraft;
}
public void setOverdraft(double overdraft) {
this.overdraft = overdraft;
}
public CheckAccount(int id, double balance, double annualInterestRate, double overdraft) {
super(id, balance, annualInterestRate);
this.overdraft = overdraft;
}
@Override
public void withdraw(double amount) {
if (amount <= getBalance()) {
// 方式一
// setBalance(getBalance() - amount);
super.withdraw(amount);
} else if (overdraft >= amount - getBalance()) {
overdraft -= (amount - getBalance());
setBalance(0);
} else {
System.out.println("超过可透支限额");
}
}
}
测试:写一个用户程序测试 CheckAccount 类。在用户程序中,创建一个账号为 1122、余额为 20000、年利率 4.5%,可透支限额为 5000 元的 CheckAccount 对象。使用 withdraw 方法提款 5000 元,并打印账户余额和可透支额。再使用 withdraw 方法提款 18000 元,并打印账户余额和可透支额。再使用 withdraw 方法提款 3000 元,并打印账户余额和可透支额。
public class CheckAccountTest {
public static void main(String[] args) {
CheckAccount ch1 = new CheckAccount(1122, 20000, 0.045, 5000);
ch1.withdraw(5000);
System.out.println("您的账户余额为:" + ch1.getBalance());
System.out.println("您的可透支限额为:" + ch1.getOverdraft());
ch1.withdraw(18000);
System.out.println("您的账户余额为:" + ch1.getBalance());
System.out.println("您的可透支限额为:" + ch1.getOverdraft());
ch1.withdraw(3000);
System.out.println("您的账户余额为:" + ch1.getBalance());
System.out.println("您的可透支限额为:" + ch1.getOverdraft());
}
}
// 输出
您的账户余额为:15000.0
您的可透支限额为:5000.0
您的账户余额为:0.0
您的可透支限额为:2000.0
超过可透支限额
您的账户余额为:0.0
您的可透支限额为:2000.0
1.2.继承性规定
一个类可被多个类继承。Java单继承性:一个类只能有一个直接父类;子父类是相对概念。
多层继承:子类直接继承的父类,称为直接父类;间接继承的父类称为间接父类;子类继承父类以后,就获取了直接父类以及所有间接父类中声明的属性和方法。
没显式声明一个类的父类,则此类继承于java.lang.Object类;所有java类(除Object外)都直接或间接继承于Object类,所有的Java类具有Object类声明的功能。
2.方法重写ovrride / overwrite
定义:在子类中对从父类中继承的方法进行改造,也称为方法的重置、覆盖。在程序执行时,子类的方法将覆盖父类的方法。
要求:子父类被重写方法必须具有相同的方法名称、参数列表;子类返回值类型和抛出的异常不能大于父类的;子类的访问权限不能小于父类的:不能重写父类权限为private的方法。
子父类中同名同参数非static为重写方法,static的不是重写属于类,子类无法覆盖父类方法。
子类中的叫重写的方法,父类中的叫被重写的方法。
重写方法返回值:父类void—>子类void、A类—>A类后A类子类、double—>double。
public class Person {
String name;
int age;
public Person() {
}
public Person(String name, int age) {
this.age = age;
this.name = name;
}
public void eat() {
System.out.println("吃饭");
}
public void walk(int distance) {
System.out.println("走路,走了" + distance);
show();
eat();
}
private void show(){
System.out.println("我是一个人");
}
public Object info(){
return null;
}
}
public class Student extends Person {
String major;
public Student() {
}
public Student(String major) {
this.major = major;
}
public void study() {
System.out.println("学习,专业是:" + major);
}
// 方法的重写
public void eat() {
System.out.println("学生吃的要有营养");
}
public void show() {
System.out.println("我是一个学生");
}
public String info() {
return null;
}
}
// 测试类
public class PersonTest {
public static void main(String[] args) {
Student student = new Student("计算机科学与技术");
student.eat();
student.walk(10);
student.study();
student.show();
System.out.println("--------------------------");
student.walk(10);
student.show();
System.out.println("-------------------------");
Person person = new Person();
person.walk(12);
}
}
// 输出
学生吃的要有营养
学习,专业是:计算机科学与技术
我是一个学生
--------------------------
走路,走了10
我是一个人
学生吃的要有营养
我是一个学生
-------------------------
走路,走了12
我是一个人
吃饭
3.supper关键字
Java类中用super调用父类结构:可用于访问父类中定义的属性(super.属性名)、成员方法(uper.方法名())和构造器(super(参数))。当子父类出现同名成员时,用super声明是父类的成员;super追溯不仅限于直接父类;父类内存空间标识。
super和this作用类似,将被屏蔽的成员变量、成员方法变为可见可用。在子类中,super访问直接父类中被屏蔽的内容,进一步提高代码重用性和灵活性,super无法访问父类的private成员。
3.1.调用父类构造器
子类中所有的构造器默认都访问父类中空参构造器;当父类无空参构造器时,子类构造器必须使用this(参数列表)或super(参数列表)指定调用本类或父类中构造器,如果子类构造器既未显式其他构造器,且父类无空参构造器编译出错。
super(形参列表)必须在子类构造器首行;类的构造器中,this(形参列表)或super(形参列表)只能二选一;构造器首行,没this(形参列表)或super(形参列表),则默认调用父类中空参构造器。
3.2.this和super区别
No. | 区别点 | this | super |
1 | 访问属性 | 访问本类属性,本类无此属性则从父类中查找 | 直接访问父类中的属性 |
2 | 调用方法 | 访问本类方法,本类无此方法则从父类查找 | 直接访问父类中的方法 |
3 | 调用构造器 | 调用本类构造器必须放在构造器首行 | 调用父类构造器必须在子类构造器首行 |
3.3.super使用
子类方法或构造器中,super.属性或super.方法调用父类属性或方法,习惯省略super.。子父类定义同名属性时,在子类中调用父类属性,必须显式用super.属性调用,表明是父类属性;重写父类方法后,在子类中调用父类方法时,必须使用super.方法调用的。表明是父类方法。
// 父类
public class Person {
String name;
int age;
int id = 1001;//证件号
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person(String name) {
this.name = name;
}
public void eat() {
System.out.println("吃饭");
}
public void walk() {
System.out.println("走路");
}
}
// 子类
public class Student extends Person {
String major;
int id = 1002;// 学号
public Student() {
}
public Student(String major) {
this.major = major;
}
public Student(String name, int age, String major) {
super(name,age);
this.major = major;
}
public void study() {
System.out.println("学习,专业是:" + major);
}
// 方法的重写
@Override
public void eat() {
System.out.println("学生吃的要有营养");
}
public void show() {
System.out.println("name = " + this.name + "\tage = " + super.age);
System.out.println("id = " + this.id);
System.out.println("id = " + super.id);
}
}
// 测试类
public class SuperTest {
public static void main(String[] args) {
Student s = new Student();
s.show();
System.out.println();
s.study();
Student s2 = new Student("Tom",21,"IT");
s2.show();
}
}
// 输出
name = null age = 0
id = 1002
id = 1001
学习,专业是:null
name = Tom age = 21
id = 1002
id = 1001
3.4.继承下子变量|函数|构造函数
变量:子父类出现同名变量时,执行子类对象结果:子类对象的值。如果想访问父类的属性或方法,可使用super加.来访问;
函数|方法:子父类方法重写时:如果想访问父类方法,用super加.来访问。
方法重写的条件:两个方法不在同一个类且有继承或实现关系;子父类的方法相同,主体可不同;子类方法的访问权限要大于父类才可以重写。
构造方法:子类的构造器一定直接或间接调用父类构造器:子类可继承所有父类非私有的属性或方法,当子类在进行初始化的时需看父类属性或方法的初始化。
3.5.子类对象实例化全过程
结果:子类继承父类后,就获取了父类中声明的属性或方法。创建子类对象时,堆空间中会加载所有父类中声明的属性。
过程:当子类构造器创建子类对象时,一定会直接或间接的调用其父类的构造器,直到调用Object类中空参的构造器为止,加载了所有父类结构,子类对象才可考虑进行调用父类结构。
明确:虽然创建子类对象时调用了父类构造器,但自始至终就创建过一个对象,即new对象。
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);
}
}
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();// 20
}
}
3.6.继承是构造方法调用顺序
条件 | 顺序 |
子类构造方法无显示supper调用,父类有参构造方法也无this调用自身其他构造器 | 默认先调用父类的无参构造方法 |
子类构造器使用supper调用父类有参构造 | 执行父类相应构造器不执行父类无参构造 |
子类构造使用this调用自身其他构造方法 | 相应构造方法中应用以上两条规则 |
如果存在多级继承关系,创建子类对象时,以上规则多次向更高一级父类应用,一直执行顶级父类Object类无参构造为止 |
4.面向对象特征之多态性
对象的多态性:父类的引用指向子类的对象|子类的对象赋给父类的引用,可直接应用在抽象类和接口上。
Java引用变量类型:编译时类型和运行时类型。编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。简称:编译时,看左边;运行时,看右边。若编译时类型和运行时类型不一致时,出现对象的多态性(Polymorphism);多态看左边:看父类的引用,即父类中不具备子类特有方法;看右边:看子类对象,实际运行的是子类重写父类的方法。
在Java中,子类的对象可替代父类的对象使用。一个变量只能有一种确定的数据类型;一个引用类型变量可能指向(引用)多种不同类型的对象。
多态:同一个引用类型,使用不同实例而执行不同操作。方法重写是实现多态的基础。
<父类型> <引用变量名> = new <子类型>(),通过父类引用变量调用的方法是子类覆盖或继承父类的方法,不是父类的方法;无法使用父类引用变量调用子类特有方法。Pet pet = new Dog()向上转型(upcasting):父类的引用指向子类对象,自动类型转换。
<子类型> <引用变量名> = (<子类型>)<父类型的引用变量>,在向下转型的过程中,如果没有转换为真实子类类型,会出现类型转换异常。Dog dog = (Dog)pet向下转型:将一个指向子类对象的父类引用赋给一个子类引用,即:父类类型转换为子类类型。需强制类型转换。
Person p = new Student();
Object o = new Person();// Object类型的变量o,指向Person类型的对象
o = new Student(); // Object类型的变量o,指向Student类型的对象
子类可看做特殊的父类,所以父类类型的引用可指向子类的对象:向上转型。
一个引用类型变量如果声明为父类的类型,但实际引用的是子类对象,那么该变量就不能再访问子类中添加的属性和方法
Student m = new Student();
m.school = “pku”; // 合法,Student类有school成员变量
Person e = new Student();
e.school = “pku”; // 非法,Person类没有school成员变量
属性是在编译时确定的,编译时e为Person类型,没有school成员变量,因而编译错误。
虚拟方法调用(Virtual Method Invocation):多态情况下,子类重写父类方法,将父类的方法称为虚拟方法,父类根据赋给它不同子类对象,动态调用子类方法,调用在编译期是无法确定。
编译时类型和运行时类型:编译时父类型,方法调用在运行时确定,所以调用子类的方法。——动态绑定。
多态使用前提:继承关系和方法重写;多态性只适用于方法,不适用于属性,属性编译和运行都看左边。
// 父类
public class Person {
String name;
int age;
public void eat() {
System.out.println("人:吃饭");
}
public void walk() {
System.out.println("人:走路");
}
}
// 子类Man
public class Man extends Person {
boolean isSmoking;
public void earnMoney() {
System.out.println("男人负责挣钱养家");
}
public void eat() {
System.out.println("男人多吃肉,长肌肉");
}
public void walk() {
System.out.println("男人霸气的走路");
}
}
// 子类Woman
public class Woman extends Person {
boolean isBeauty;
public void goShopping() {
System.out.println("女人喜欢购物");
}
public void eat() {
System.out.println("女人少吃,减肥");
}
public void walk() {
System.out.println("女人窈窕走路");
}
}
public class PersonTest {
public static void main(String[] args) {
// 对象的多态性:父类的引用执行子类的对象
Person p2 = new Man();
// 多态的使用:当调用子父类同名同参数的方法时,实际执行的是子类重写父类的方法-虚拟方法调用
p2.eat();
p2.walk();
System.out.println("***************************");
// 不能调用子类特有的方法、属性:编译时,p2是Person类型
p2.name = "Tom";
// p2.earnMoney;
// p2.isSmoking;
// 有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致编译时只能调用父类中声明的属性和方法,子类特有的属性和方法不能调用。
// 如何调用子类特有的属性和方法
// 向下转型,使用强制类型转换符,多态
Man m1 = (Man) p2;
m1.earnMoney();
m1.isSmoking = true;
// 使用强转时,可能出现ClassCastException的异常。
// 编译通过,运行抛出ClassCastException异常,类型转换错误(男人类不能转换成女人类)
// 练习一
// Woman w1 = (Woman)p2;
// w1.goShopping();
// 练习二
// Person p3 = new Person();
// Man man2 = (Man)p3;
// 运行编译都通过
Object object = new Woman();
Person person = (Person) object;
}}
5.方法重载和重写
重载指允许存在多个同名方法,而这些方法参数不同。编译器根据方法不同的参数表,对同名方法的名称做修饰。对于编译器而言,这些同名方法就成了不同的方法,调用地址在编译期就绑定。Java的重载可包括父类和子类,即子类可重载父类的同名不同参数的方法。
重载方法调用前,编译器就已经确定要调用的方法,称为“早绑定”或“静态绑定”;而对于多态,只有等到方法调用,解释运行器才确定要调用的具体方法,这称为“晚绑定”或“动态绑定”。
6.instanceof 操作符
x instanceof A:检验x是否为类A的对象|实例,是返回true,不是返回false;如果x类是A的子类B,x instanceof A值也为true。
使用instanceof时,对象类型必须和instanceof后面的参数指定的类有父子关系。
使用情景:避免在向下转型时出现ClassCastException的异常,在向下转型前,先进行类型判断,返回true就进行向下转型,返回false就不进行向下转型。如果a instanceod A返回true,则a instanceof B也返回true, 类B和类A是子父类。
if (p2 instanceof Woman) {
Woman w1 = (Woman) p2;
w1.goShopping();
System.out.println("*******************Woman**************");
}
if (p2 instanceof Man) {
Man m2 = (Man) p2;
m2.earnMoney();
System.out.println("*******************Man**************");
}
if (p2 instanceof Person) {
System.out.println("*******************Person**************");
}
if (p2 instanceof Object) {
System.out.println("*******************Object**************");
}
对Java对象的强制类型转换称为造型:从子类到父类的类型转换可以自动进行;从父类到子类的类型转换必须通过造型(强制类型转换)实现;无继承关系的引用类型间的转换是非法的;在造型前可使用instanceof操作符测试一个对象的类型。
8.Object类
方法 | 说明 |
---|---|
clone() | 复制一个新对象 |
equals(Object obj) | 比较两个对象是否相等 |
finalize() | 回收不使用的对象 |
notify() | 激活等待的线程 |
wait(long timeout) | 使当前线程等待 |
toString() | 返回对象的详细信息 |
Object类是所有Java类根父类;在类声明时未使用extends关键字指明父类,则默认父类为java.lang.Object类;Object类中的功能(属性、方法)具有通用性;Object的finalize方法,在对象回收前调用,垃圾回收机制会自己调用,Object无属性。Object类的clone()的使用
public class CloneTest {
public static void main(String[] args) {
Animal a1 = new Animal("花花");
try {
Animal a2 = (Animal) a1.clone();
System.out.println("原始对象:" + a1);
a2.setName("毛毛");
System.out.println("clone之后的对象:" + a2);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
class Animal implements Cloneable{
private String name;
public Animal() {
super();
}
public Animal(String name) {
super();
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Animal [name=" + name + "]";
}
@Override
protected Object clone() throws CloneNotSupportedException {
// TODO Auto-generated method stub
return super.clone();
}
}
方法名称 | 类型 | 描述 |
public Object() | 构造 | 构造器 |
public int hashCode() | 普通 | 取得Hash码 |
public class ObjectTest {
public static void main(String[] args) {
Order order = new Order();
// 输出:class java.lang.Object
System.out.println(order.getClass().getSuperclass());
}
}
class Order{
}
8.1.toString()
toString()方法在Object类中定义,返回值是String类型,返回类名和它的引用地址。当输出一个对象的引用时,实际上就是调用当前对象的toString()。
Object类中toString()的定义
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
像String、Date、File、包装类等都重写了Object类中的toString()方法,使得在调用对象的toString()时,返回实体内容信息 ;自定义类也可重写toString()方法,根据需要返回对象的内容。
public class ToStringTest {
public static void main(String[] args) {
String aString = "dj";
aString.toString();
ToStringTest dStringTest = new ToStringTest();
dStringTest.toString();
}
}
// 输出
dj
com.atguigu1.java1.ToStringTest@15db9742
8.2.==和equals
==是运算符,可比较基本数据类型和引用数据类型;equals()是方法,而非运算符;只适用于引用数据类型。
==:基本类型:两个变量的值相等,即为true;引用类型:两个引用指向同一个对象实体时返回true。符号两边的数据类型必须兼容,可自动转换的基本数据类型除外,否则编译出错。
equals():所有类都继承Object,就继承equals()方法,可重写,只能比较引用类型,其作用与==相同,比较是否指向同一个对象;File、String、Date及包装类等,比较类型及内容而不考虑引用的是否是同一个对象。原因:在这些类中重写了Object类的equals()方法。重写自定义equals时,根据重写规则比较两个对象是否相等。
// 父类
public class GeometricObject {
protected String color;
protected double weight;
// 省略getter、setter和构造器
}
// 子类
public class Circle extends GeometricObject {
private double radius;
public Circle() {
super();
radius = 1.0;
// color = "white";
// weight = 1.0;
}
public Circle(double radius) {
super();
this.radius = radius;
}
public Circle(double radius,String color,double weight) {
super(color,weight);
this.radius = radius;
}
// 省略getter、setter方法
//求圆的面积
public double findArea(){
return 3.14 * radius * radius;
}
//比较两个圆的半径是否相等,如相等,返回true。
@Override
public boolean equals(Object obj) {
if(this == obj){
return true;
}
if(obj instanceof Circle){
Circle c = (Circle)obj;
return this.radius == c.radius;
}
return false;
}
@Override
public String toString() {
return "Circle [radius=" + radius + "]";
}
}
测试:写一个测试类,创建两个Circle对象,判断其颜色是否相等;利用equals方法判断其半径是否相等;利用toString()方法输出其半径。
public class CircleTest {
public static void main(String[] args) {
Circle circle1 = new Circle(2.3);
Circle circle2 = new Circle(3.3, new String("white"), 2.0);
System.out.println("颜色是否相等:" + circle1.getColor().equals(circle2.getColor()));
System.out.println("半径是否相等:" + circle1.equals(circle2));
System.out.println(circle1);
System.out.println(circle2.toString());
}
}
// 输出
颜色是否相等:true
半径是否相等:false
Circle [radius=2.3]
Circle [radius=3.3]
重写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。
public class EqualsTest {
private int age;
private String name;
// Object的equals方法进行重写,手动重写
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof String) {
EqualsTest cut = (EqualsTest) obj;
// 比较连个对象属性是否相同
// if (this.age == cut.age && this.name.equals(cut.name)) {
// return true;
// } else {
// return false;
// }
// 或
return this.age == cut.age && this.name.equals(cut.name);
}
return false;
}
public static void main(String[] args) {
String id = "34";
id.equals("34");
}
}
9.Java的Junit单元测试
步骤:选中当前工程-右键选择:build path - add libraries - Junit 4 -下一步;创建Java类,进行单元测试。 Java类要求:类是public且类提供公共无参构造器
声明单元测试:单元测试方法上需要声明注解@Test,并在单元测试类中导入:import org.junit.Test;;声明好单元测试方法后,就可在方法体内测试相关的代码;写完后,左键双击单元测试方法名,右键:run as-JUnit Test
说明:如果执行结构没有任何异常:绿条;如果执行结果出现异常:红条。
@Test
public void testEquals() {
String s1 = "MM";
String s2 = "MM";
System.out.println(s1.equals(s2));
}
10.Vector
Vector代替数组处理:键盘读入学生成绩(负数代表结束),找出最高分,并输出学生成绩等级。数组长度一旦确定,就无法修改,而向量类java.util.Vector可根据需要数据长度动态伸缩。
创建Vector对象:Vector v=new Vector(),给向量添加元素:v.addElement(Object obj); //obj必须是对象;取出向量中的元素:Object obj=v.elementAt(0);第一个元素的下标是0,返回值是Object类型的。计算向量的长度:v.size();若与最高分相差10分内:A等;20分内:B等;30分内:C等;其它:D等
public class ScoreTest {
public static void main(String[] args) {
// 1.实例化Scanner,用于从键盘获取学生成绩
Scanner scan = new Scanner(System.in);
// 2.创建Vector对象:Vector v=new Vector();相当于原来的数组
Vector v = new Vector();
// 3.通过for(;;)或while(true)方式,给Vector中添加数组
int maxScore = 0;
for (;;) {
System.out.println("请输入学生成绩(以负数代表输入结束)");
int score = scan.nextInt();
// 3.2 当输入是负数时,跳出循环
if (score < 0) {
break;
}
if (score > 100) {
System.out.println("输入的数据非法,请重新输入");
continue;
}
// 3.1 添加操作::v.addElement(Object obj)
// jdk5.0之前:
// Integer inScore = new Integer(score);
// v.addElement(inScore);//多态
// jdk5.0之后:
v.addElement(score);// 自动装箱
// 4.获取学生成绩的最大值
if (maxScore < score) {
maxScore = score;
}
}
// 5.遍历Vector,得到每个学生的成绩,并与最大成绩比较,得到每个学生的等级。
char level;
for (int i = 0; i < v.size(); i++) {
Object obj = v.elementAt(i);
// jdk 5.0之前:
// Integer inScore = (Integer)obj;
// int score = inScore.intValue();
// jdk 5.0之后:
int score = (int) obj;
if (maxScore - score <= 10) {
level = 'A';
} else if (maxScore - score <= 20) {
level = 'B';
} else if (maxScore - score <= 30) {
level = 'C';
} else {
level = 'D';
}
System.out.println("student-" + i + " score is " + score + ",level is " + level);
}
}
}
11.native关键字
native关键字说明这个方法是原生函数,是用 C/C++等非Java 语言实现的,并且被编译成了 DLL,由 java 去调用。
有时java应用需要与 java 外面的环境交互。这是本地方法存在的主要原因,如:java 需要与一些底层系统如操作系统或某些硬件交换信息时。本地方法正是这样一种交流机制:它为我们提供了一个非常简洁的接口,而且无需去了解 java 应用之外的繁琐的细节。
native 声明的方法,对于调用者,可当做和其他 Java 方法一样使用。native method 方法可以返回任何 java 类型,包括非基本类型,而且同样可进行异常控制。
native method 的存在并不会对其他类调用这些本地方法产生任何影响,实际上调用这些方法的其他类甚至不知道它所调用的是一个本地方法。JVM 将控制调用本地方法的所有细节。如果一个含有本地方法类被继承,子类会继承这个本地方法且可用java语言重写该方法。
12.类与类之间关系
12.1.依赖关系(Dependency)
对象之间最弱的一种关联方式,临时性关联。代码中一般指由局部变量、函数参数、返
回值建立的对于其他对象的调用关系。
class A{
public B method(C c,D d){
E e = new E();
...
B b = new B();
...
return b;
}
}
这个代码结构中,表示 A 类依赖了 B,C,D,E 类
12.2.关联关系(Association)
对象之间一种引用关系,比如客户类与订单类之间的关系。这种关系通常使用类的属性表达。关联可以有方向,即导航。一般不作说明的时候,导航是双向的,不需要在线上标出箭头。
class Employee{
private int eid;//员工编号
private String name;//员工姓名
private Computer coumputer;//员工所使用的电脑
//....
}
class Computer{
}
关联表示类之间的持久关系,一般表示一种重要的业务之间的关系,需要保存,或者说需要持久化,或者说需要保存到数据库。依赖类之间的是一种临时、短暂关系,不需要保存的。
12.3.聚合(Aggregation)
聚合(关联关系的一种):表示 has-a 的关系。与关联关系一样,聚合关系也是通过实例变量来实现这样关系的。关联关系和聚合关系来语法上是没办法区分,从语义上才能更好的区分两者的区别。如汽车类与引挚类,轮胎类之间的关系就是整体与个体的关系。与关联关系一样,聚合关系也是通过实例变量来实现的。空心菱形。
class Car{
private Engine engine;//引擎
private Tyre[] tyres;//轮胎
}
关联和聚集(聚合)的区别:关联关系所涉及的两个对象是处在同一个层次上的。比如人和自行车就是一种关联关系,而不是聚合关系,因为人不是由自行车组成的;聚合关系涉及的两个对象处于不平等的层次上,一个代表整体,一个代表部分。比如电脑和它的显示器、键盘、主板以及内存就是聚集关系,因为主板是电脑的组成部分。
12.4.组合(Composite)
对象A包含对象B,对象B离开对象A没有实际意义,是一种更强的关联关系。人包含手,手离开人的躯体就失去应有作用。组合:表示 contains-a 的关系,是一种强烈的包含关系。组合类负责被组合类的生命周期。也使用属性表达组合关系,是关联关系的一种,是比聚合关系强的关系。
class Window{
private Menu menu;//菜单
private Slider slider;//滑动条
private Panel panel;//工作区
}
13.5.继承(Generalization,又称为泛化,is-a 的关系)
类与类的继承关系,类与接口的实现关系。场景:父与子、动物与人、植物与树。