前言
本小节讲解了java语言基础语法中有关类的知识,其中的图来自慕课网学习截图。
同时,制作了一个阶段性的小程序——“答答租车”小程序。
一、有关于类的核心知识点
- 类是对象的类型,是具有相同属性和方法的一组对象的集合
- 属性的概念:对象具有的各种特征,每个对象的每个属性都拥有特定值
- 方法的概念:对象执行的操作
- 类与对象的关系:类是抽象的概念,而对象是一个具体的实体,对象是类的实例化和具体化。
- 所有Java程序都以类(class)为组织单元
- 定义一个类的步骤:
· 定义类名
· 编写类的属性
· 编写类的方法
例:
public class 类名{
// 定义属性部分(成员变量)
属性1的类型 属性1;
属性2的类型 属性2;
...
属性n的类型 属性n;
// 定义方法部分
方法1;
方法2;
...
方法m;
}
- 使用Java中的对象的步骤:
· 创建对象:类名 对象名 = new 类名();
· 使用对象:引用对象的属性:对象名.属性;
引用对象的方法:对象名.方法名();
- 成员变量与局部变量
· 成员变量:在类中定义,用来描述对象的属性。成员变量的作用域在整个类内部都是可见的。成员变量的默认值为0。
· 局部变量:在类的方法中定义,在方法中临时保存数据,用来做一些数据的处理等。局部变量的作用域仅限于定义它的方法。Java并不会给局部变量赋予初始值。
· 在同一个方法中,不允许有同名局部变量;在不同的方法中,可以有同名局部变量
· 两类变量同名时,局部变量具有更高的优先级,即‘就近原则’。 - Java语言中的构造方法
· 使用new + 构造方法
创建一个新的对象
· 构造方法是定义在Java类中的一个用来初始化对象的成员变量的方法;构造方法与类同名且没有返回值。
· 当没有指定构造方法时,系统会自动添加无参的构造方法
· 当有指定构造方法,无论是有参、无参的构造方法,都不会自动添加无参的构造方法。
· 构造方法的重载:方法名相同,但参数不同的多个方法,调用时会自动根据不同的参数选择相应的方法。
· 构造方法不但可以给对象的属性赋值,还可以保证给对象的属性赋一个合理的值
· 语法格式:
public 构造方法名(){
// 初始化代码
}
- Java语言中的静态成员(类成员):被static修饰的成员称为静态成员或类成员。它属于整个类所有,而不是某个对象所有,即被类的所有对象所共享。静态成员可以使用类名直接访问,也可以使用对象名进行访问。static可以修饰变量、方法和代码块。
- Java中可以通过初始化块进行数据赋值。如:
public class HelloWorld{
String name; // 定义一个成员变量
// 通过初始化块为成员变量赋值
{
name = “你好”;
}
}
在类的声明中,可以包含多个初始化块,当创建类的实例时,就会依次执行这些代码块。如果使用static修饰初始化块,就称为静态初始化块。
二、面向对象的三大特性之封装
- 封装概念:将类的某些信息隐藏在类内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问。即:隐藏属性,通过方法来得到属性的值。
- 封装好处:只能通过规定的方法访问数据;隐藏类的实例细节,方便修改和实现。
- 封装的实现步骤:
· 修改属性的可见性
· 创建getter/setter方法
· 在getter/setter方法中加入属性控制语句,对属性值的合法性进行判断 - Java中的包
· 包的作用:管理Java文件,解决同名文件冲突的问题
· 定义包:package 包名
· 包的使用:
1)可以通过import关键字,在某个文件使用其它文件中的类,如:import java.util.Scanner;
2)在Java中,包的命名规范是全小写字母拼写。 - Java中的访问修饰符:可以修饰属性和方法的访问范围
- Java中的this关键字
· this关键字代表当前对象
1)this.属性 操作当前对象的属性
2)this.方法 调用当前对象的方法
· 封装对象的属性的时候,经常会使用this关键字,例如在使用getter/setter方法的时候,常常会碰到‘参数名’与‘属性名’相同的情况,这时我们可以通过在属性名之前加上this关键字来加以区分。 - Java中的内部类
· 概念:定义在另外一个类里面的类称为内部类;与之对应,包含内部类的类称为外部类。
· 内部类的主要作用:
1)提供了更好的封装,可以把内部类隐藏在外部类之内,不允许同一个包中的其他类访问该类
2)内部类的方法可以直接访问外部类的所有数据,包括私有的数据
3)内部类所实现的功能使用外部类同样可以实现,只是有时使用内部类更方便
· 内部类可分为以下四种:
1)成员内部类(又称为普通内部类)
2)静态内部类
3)方法内部类
4)匿名内部类 - 内部类之成员内部类
· 使用方法:
1)内部类相当于外部类的一个成员变量的位置,可以使用任意访问控制符,如public、protected、private等
2)内部类中的方法可以直接访问外部类中的数据,而不受访问控制符的影响。
3)定义了成员内部类后,必须使用外部类对象来创建内部类对象,而不能直接去new一个内部类对象,即:内部类 对象名 = 外部类对象.new 内部类();
- 内部类之静态内部类
· 特点:
1) 静态内部类不能直接访问外部类的非静态成员,但可以通过new 外部类().成员
的方式访问。
2)如果外部类的静态成员与内部类的成员名称相同,可通过“类名.静态成员”访问外部类的静态成员;如果外部类的静态成员与内部类的成员名称不相同,则可通过‘成员名’直接调用外部类的静态成员。
3)创建静态内部类的对象时,不需要外部类的对象,可以直接创建:内部类 对象名 = new 内部类();
- 内部类之方法内部类
· 方法内部类就是内部类定义在外部类的方法中,方法内部类只在该方法的内部可见,即只在该方法内可以使用。
· 由于方法内部类不能在外部类的方法以外的地方使用,因此方法内部类不能使用访问控制符和static修饰符。
三、面向对象的三大特性之继承
- 继承的概念:Java中的继承是单继承的,继承是类与类的一种关系。
- 继承的优点:
1)子类拥有父类的所有属性和方法
2)实现代码复用 - 语法规则:
class 子类 extends 父类
- 继承的初始化顺序:
1)初始化父类再初始化子类
2)先执行初始化对象中属性,再执行构造方法中的初始化
即:先进行父类对象的属性初始化,接着执行父类的构造方法,然后再进行子类对象的属性初始化,最后再执行子类的构造方法。 - 方法的重写
· 如果子类对继承父类的方法不满意,是可以重写父类继承的方法的,当调用方法时会优先调用子类的方法。
· 语法规则:返回值类型、方法名、参数类型及个数都要与父类继承的方法相同,才叫方法的重写。 - Java中的final关键字的使用
· 使用final关键字做标识有“最终的”含义
· final可以修饰类、方法、属性和变量
1)final修饰类,则该类不允许被继承
2)final修饰方法,则该方法不允许被覆盖(重写)
3)final修饰属性,则该类的属性不会进行隐式的初始化(类的初始化属性必须有值)或在构造方法中赋值(但只能选其一)
4)final修饰变量,则该变量的值只能赋一次值,即变为常量 - Java中的super关键字:在对象的内部使用,可以代表父类对象。
· 可通过super关键字访问父类的属性
· 可通过super关键字访问父类的方法
· 子类在构造的过程当中必须调用其父类的构造方法,如果子类的构造方法中没有显式调用父类的构造方法,则系统默认调用父类无参的构造方法。即:程序隐式的在子类的构造方法中添加一句‘super();’代码调用父类的构造方法。
· 如果显示的调用构造方法,必须在子类的构造方法的第一行。
· 如果子类构造方法中既没有显式调用父类的构造方法,而父类又没有无参的构造方法,则编译出错。 - Java中的Object类
· Object类是所有类的父类,如果一个类没有使用extends关键字明确标识继承另外一个类,那么这个类默认继承Object类。
· Object类中的方法,适合所有子类。
· 几个比较重要的方法
1)toString()方法:在Object类里面定义toString()方法的时候返回的对象的哈希code码(对象地址字符串)。可以通过重写toString()方法表示出对象的属性。
2)equals()方法:比较的是对象的引用是否指向同一块内存地址。当我们用‘new关键字’创建一个对象时,其实创建的是对象在内存中的地址,我们通过操作该地址间接操作了对象的值。
四、面向对象的三大特性之多态
- 多态的概念:对象的多种形态
- 继承是多态的实现基础
- 引用多态
1)父类的引用可以指向本类的对象
2)父类的引用可以指向子类的对象 - 方法多态
1)创建本类对象时,调用的方法为本类方法
2)创建子类对象时,调用的方法为子类重写的方法或者继承的方法
3)不允许通过父类的对象(引用)调用子类独有的方法 - 引用类型转换
1)向上类型转换(隐式/自动类型转换),是小类型到大类型的转换
2)向下类型转换(强制类型转换),是大类型到小类型
3)3)instanceof运算符可用来解决引用对象的类型,避免类型转换的安全性问题。在写程序时,如果想进行类型转换,为了避免程序的安全性问题,最好使用instanceof关键字来进行验证。 - Java中的抽象类
· 定义:抽象类使用前使用abstract
关键字修饰,则该类为抽象类。
· 应用场景:
1)在某些情况下,某个父类只是知道其子类应该包含怎样的方法,但无法准确知道这些子类如何实现这些方法。
2)从多个具有相同特征的类中抽象出一个抽象类,以这个抽象类作为子类的模板,从而避免了子类设计的随意性。
· 作用:限制规定子类必须实现某些方法,但不关注实现细节。
· 使用规则:
1)abstract定义抽象方法时,只有声明,不需要实现,因此其没有方法体,需要使用分号作为结束符号。
2)包含抽象方法的类称为抽象类。
3)抽象类中可以包含普通的方法,也可以没有抽象方法。
4)抽象类不能直接创建,可以定义引用变量。 - Java中的接口
· 概念:接口可以理解为一种特殊的类,由全局常量和公共的抽象方法所组成。类是一种具体实现体,而接口定义了某一批类所需要遵守的规范,接口不关心这些类的内部数据,也不关心这些类里方法的实现细节,它只规定这些类里必须提供某些方法。
· 定义:使用interface
关键字
· 接口就是用来被继承、被实现的,修饰符一般建议用public。注意:不能使用private和protected修饰接口。
· 接口中的属性是常量,即使定义时不添加public static final修饰符,系统也会自动加上。
· 接口中的方法只能是抽象方法,即使定义时不添加public abstract修饰符,系统也会自动加上。
· 基本语法:
[修饰符] [abstract] interface 接口名 [extends 父接口1, 父接口2...]
{
零个到多个常量定义...
零个到多个抽象方法的定义...
}
· 使用接口:一个类可以实现一个或多个接口,实现接口使用implements关键字。Java中一个类只能继承一个父类,是不够灵活的,通过实现多个接口可以做补充。同时,接口还经常和匿名内部类配合使用。匿名内部类就是没有名字的内部类,多用于关注实现而不关注实现类的名称。注意结尾要有分号作为结束符号。如:
Interface i = new Interface(){
...
};
· 继承父类实现接口的语法为:
[修饰符] class 类名 extends 父类 implements 接口1,接口2...
{
类体部分
}
如果要继承父类,继承父类必须在实现接口之前
补充
- float型变量赋值时需加f,否则默认浮点数是double型
- 静态成员属于整个类,当系统第一次使用该类时,就会为其分配内存空间直到该类被卸载才会进行资源回收!
- 静态方法中可以直接调用同类中的静态成员,但不能直接调用非静态成员。如果希望在静态方法中调用非静态变量,可以通过创建类的对象,然后通过对象来访问非静态变量。
- 在普通成员方法中,可以直接访问同类的非静态变量和静态变量。
- 静态方法中不能直接调用非静态方法,需要通过对象来访问非静态方法。
- 静态初始化块只在类加载时执行,且只会执行一次,同时静态初始化块只能给静态变量赋值,不能初始化普通的成员变量。
- 当程序中既有‘静态初始化块’,又有‘普通初始化块’,还有‘构造方法’的时候,程序运行时静态初始化块最先被执行,然后执行普通初始化块,最后才执行构造方法。
- Java语言在实现对类的封装中,getter/setter方法的方法名通常为:‘get’关键字+属性名,且属性名大写,如:
getAge()
- 在定义包时,包名必须放在Java源程序的第一行,包名间可以使用“.”号隔开
- 系统中的包:
1)java.(功能).(类)
2)java.lang.(类) 包含java语言基础的类
3)java.util.(类) 包含java语言中各种工具类
4)java.io.(类) 包含输入、输出相关功能的类 - 可以通过“*”导入某个包下的所有文件,如:
java.util.*;
- 在Eclipse编译器中自动生成getter/setter方法:点击菜单栏中的‘Source’或通过在代码编辑区点击鼠标右键,选择‘Source’,然后选择‘Generate Getter and Setters’,选择想要生成getter/setter方法的属性,并点击‘OK’按钮即可生成。
- 成员内部类的.class文件的命名规则为:
外部类名$内部类名.class
- 外部类是不能直接使用内部类的成员和方法的,必须先创建内部类的对象,然后通过内部类的对象来访问其成员变量和方法。
- 如果外部类和内部类具有相同的成员变量或方法,内部类默认访问自己的成员变量或方法,如果要访问外部类的成员变量,可以使用
外部类的类名.this关键字.外部类的成员变量
。 - 在Eclipse编译器中自动生成toString()方法:点击菜单栏中的‘Source’或通过在代码编辑区点击鼠标右键,选择‘Source,然后选择‘Generate toString()’,选择想要生成toString()方法的属性,并点击‘OK’按钮即可生成。
- 在Eclipse编译器中自动生成equals()方法:点击菜单栏中的‘Source’或通过在代码编辑区点击鼠标右键,选择‘Source’,然后选择‘Generate hashCode() and equals()’,选择想要生成equals()方法的属性,并点击‘OK’按钮即可生成。
- 子类必须要调用其父类的隐式构造器。所以当我们在父类中手动添加了一个含参构造器时,Java程序就不会自动为父类添加一个无参的构造器,而继承于它的子类就会报错!
答答租车的小程序
- 功能:
· 展示所有可租车辆
· 可让用户选择所租车的车型及租车量
· 展示租车清单,包含:总金额、总载货量及其车型、总载人量及其车型 - 核心代码如下所示:
package com.imooc;
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
/**
* 1)'rent_a_car'变量用于存储用户是否租车的数据
* 2)'number'变量用于存储用户想要租车的数量
* 3)'serial_number'变量用于存储用户租车的序号
* 4)'number_of_manned'变量用于存储总载人数
* 5)'number_of_laden_cargo'变量用于存储总载货数
* 6)'sum_of_price'变量用于存储租车总价格
* 7)'mannedCar_name'数组用于存储用户所选的载人汽车的名字
* 8)'laden_cargo_name'数组用于存储用户所选的载货汽车的名字
* 9)'day_of_rent'数组用于存储用户的租车天数
*/
int rent_a_car; // 用于存储用户是否租车的数据
int number; // 用于存储用户想要租车的数量
int serial_number; // 用于存储用户租车的序号
int number_of_manned = 0; // 用于存储总载人数
double number_of_laden_cargo = 0; // 用于存储总载货数
double sum_of_price = 0; // 用于存储租车总价格
String [] mannedCar_name; // 用于存储用户所选的载人汽车的名字
String [] laden_cargo_name; // 用于存储用户所选的载货汽车的名字
int day_of_rent; // 用于存储用户的租车天数
/**
* 生成六种汽车的实例对象,并在构造方法中进行初始化
*/
MannedCar Audi = new MannedCar("奥迪A4", 500, 4);
MannedCar Mazda = new MannedCar("马自达6", 400, 4);
Pickup Picca_snow = new Pickup("皮卡雪6", 450, 4, 2);
MannedCar golden_dragon = new MannedCar("金龙", 800, 20);
Truck Songhua_river = new Truck("松花江", 400, 4);
Truck Iveco = new Truck("依维柯", 1000, 20);
/**
* 给用户的一些提示信息
*/
System.out.println("欢迎使用答答租车系统:");
System.out.println("您是否要租车: 1是 0否");
/**
* 对'Scanner'类进行实例化
*/
Scanner input = new Scanner(System.in);
// 将用户输入的是否要租车的信息存入'rent_a_car'变量中
rent_a_car = input.nextInt();
// 对用户输入的是否要租车的信息进行判断
if(rent_a_car == 1){
/**
* 给用户的一些提示信息——租车的类型及其价目表
*/
System.out.println("您可租车的类型及其价目表:");
System.out.println("序号\t汽车名称\t\t租金\t\t容量");
System.out.println("1.\t奥迪A4\t\t500元/天\t\t载人:4人");
System.out.println("2.\t马自达6\t\t400元/天\t\t载人:4人");
System.out.println("3.\t皮卡雪6\t\t450元/天\t\t载人:4人 载货2吨");
System.out.println("4.\t金龙\t\t800元/天\t\t载人:20人");
System.out.println("5.\t松花江\t\t400元/天\t\t载货:4吨");
System.out.println("6.\t依维柯\t\t1000元/天\t载货:20吨");
System.out.println("请输入您要租汽车的数量:");
// 1)将用户输入的要租汽车的数量信息存入'number'变量中
// 2)同时在内存中分配(开辟)两份存储空间,每份number个存储空间
// 3)一份存储载人汽车的汽车名称,一份存储载货汽车的汽车名称
number = input.nextInt();
mannedCar_name = new String[number];
laden_cargo_name = new String[number];
// 对用户输入的'number'变量进行for循环遍历
for(int i = 1; i <= number; i++){
// 每一次循环让用户输入第i辆车的序号,并将序号存储进'serial_number'变量中
System.out.println("请输入第" + i + "俩车的序号:");
serial_number = input.nextInt();
/**
* · 对用户输入的第i俩车的序号进行判断
* · 一共要进行六次判断
*/
if(serial_number == 1){
mannedCar_name[i-1] = Audi.car_name;
number_of_manned += Audi.manned_capacity;
sum_of_price += Audi.rent;
}
if(serial_number == 2){
mannedCar_name[i-1] = Mazda.car_name;
number_of_manned += Mazda.manned_capacity;
sum_of_price += Mazda.rent;
}
if(serial_number == 3){
mannedCar_name[i-1] = Picca_snow.car_name;
laden_cargo_name[i-1] = Picca_snow.car_name;
number_of_manned += Picca_snow.manned_capacity;
number_of_laden_cargo += Picca_snow.freight_volume;
sum_of_price += Picca_snow.rent;
}
if(serial_number == 4){
mannedCar_name[i-1] = golden_dragon.car_name;
number_of_manned += golden_dragon.manned_capacity;
sum_of_price += golden_dragon.rent;
}
if(serial_number == 5){
laden_cargo_name[i-1] = Songhua_river.car_name;
number_of_laden_cargo += Songhua_river.freight_volume;
sum_of_price += Songhua_river.rent;
}
if(serial_number == 6){
laden_cargo_name[i-1] = Iveco.car_name;
number_of_laden_cargo += Iveco.freight_volume;
sum_of_price += Iveco.rent;
}
}
// · 用于提示用户输入租车天数,并将用户输入的信息存储进'day_of_rent'变量中
// · 并将其与'sum_of_price'变量的值相乘得出“租车总价格”
System.out.println("请输入租车天数:");
day_of_rent = input.nextInt();
sum_of_price *= day_of_rent;
// 将计算结果反馈(展现)给用户
System.out.println("您的账单:");
System.out.println("***可载人的车有:");
/**
* 使用for循环遍历mannedCar_name[number]数组里面的数据
*/
for(int i = 0; i < mannedCar_name.length; i++){
// 当mannedCar_name[i]里面的数据为空时,跳过此次循环,即:不将本次结果展现给用户
if(mannedCar_name[i] == null){
continue;
}
System.out.print("\t" + mannedCar_name[i]);
}
// 将载人汽车总的载人人数展示给用户
System.out.println("\t共载人:" + number_of_manned + "人");
System.out.println("***载货的车有:");
/**
* 使用for循环遍历laden_cargo_name[number]数组里面的数据
*/
for(int i = 0; i < laden_cargo_name.length; i++){
// 当laden_cargo_name[i]里面的数据为空时,跳过此次循环,即:不将本次结果展现给用户
if(laden_cargo_name[i] == null){
continue;
}
System.out.print("\t" + laden_cargo_name[i]);
}
// 将载货汽车总的载货货数展示给用户
System.out.println("\t共载货:" + number_of_laden_cargo + "吨");
// 将租车总价格展示给用户
System.out.println("***租车总价格:" + sum_of_price + "元");
}else{
System.out.println("See you next time");
}
}
}
- 程序运行结果如下:
欢迎使用答答租车系统:
您是否要租车: 1是 0否
1
您可租车的类型及其价目表:
序号 汽车名称 租金 容量
1. 奥迪A4 500元/天 载人:4人
2. 马自达6 400元/天 载人:4人
3. 皮卡雪6 450元/天 载人:4人 载货2吨
4. 金龙 800元/天 载人:20人
5. 松花江 400元/天 载货:4吨
6. 依维柯 1000元/天 载货:20吨
请输入您要租汽车的数量:
3
请输入第1俩车的序号:
3
请输入第2俩车的序号:
6
请输入第3俩车的序号:
4
请输入租车天数:
10
您的账单:
***可载人的车有:
皮卡雪6 金龙 共载人:24人
***载货的车有:
皮卡雪6 依维柯 共载货:22.0吨
***租车总价格:22500.0元