7.1 类的封装
封装是面向对象编程的核心思想。同时我们也知道类是载体,只不过我们把对象的属性和行为封装在载体中。
例:顾客去一家餐饮吃饭,点了一份香辣肉丝。
public class seven1 {
public static void main(String[] args) {
String cookName="Tom Cruise";//厨师的名字叫Tom Cruise
System.out.println("**请让厨师为我做一份香辣肉丝。***");
System.out.println(cookName + "切葱花");
System.out.println(cookName + "洗蔬菜");
System.out.println(cookName + "开始烹饪" + "香辣肉丝");
System.out.println("**请问厨师叫什么名字?***");
System.out.println(cookName);
System.out.println("**请让厨师给我切一点葱花。***");
System.out.println(cookName + "切葱花");
}
}
运行结果如下:
现我们用封装的方式来实现:
public class seven2 {
public static void main(String[] args) {
Cook1 cook = new Cook1();// 创建厨师类的对象
System.out.println("**请让厨师为我做一份香辣肉丝。***");
cook.cooking("香辣肉丝");// 厨师烹饪香辣肉丝
System.out.println("**你们的厨师叫什么名字?***");
System.out.println(cook.name);// 厨师回答自己的名字
System.out.println("**请让厨师给我切一点葱花。***");
cook.cutOnion();// 厨师去切葱花
}
}
class Cook1 {
String name;// 厨师的名字
public Cook1() {
this.name = "Tom Cruise";// 厨师的名字叫Tom Cruise
}
void cutOnion() {// 厨师切葱花
System.out.println(name + "切葱花");
}
void washVegetavles() {// 厨师洗蔬菜
System.out.println(name + "洗蔬菜");
}
void cooking(String dish) {// 厨师烹饪顾客点的菜
washVegetavles();
cutOnion();
System.out.println(name + "开始烹饪" + dish);
}
}
运行结果如下:
7.2 类的继承
继承的思想就是子类可以继承父类原有的属性和方法,也可以增加父类所不具备的属性和方法,或者直接重写父类中的某些方法。
1.extennds关键词
继承是让一个类继承另一个类,只需要使用enxtends关键字就可以了,同时也要注意java中仅支持单一继承,也就是一个类只有一个父类。
上面代码可以得出一个结论,继承只是关系。父类和子类都可以进行实例化,子类对象可以去使用父类的属性和方法,而不要做具体实现。同时子类也可以扩展自己的属性和方法。关于父类属性和方法被子类对象使用就是代码重复性
2.方法的重写
父类的成员都会被子类继承,当父类的某个方法并不适用于子类时,就需要在子类重写父类的这个方法。
重写的实现
继承不只是扩展父类的功能,还可以重写父类的成员方法。重写可以理解为覆盖,就是在子类中将父类的成员方法名称保留,再重新编写父类成员方法的实现内容,更改成员方法的储存权限,或修改成员方法的返回数据类型。super 关键字
如果子类重写了父类的方法之后,子类还想调用父类方法可以使用super关键字来调用父类的方法。在子类里super关键字代表父类对象。
3.所有类的父类 —— Object 类
在java中所有的类都直接或者间接继承了java.lang.Object类。也称Object为基类。所以Object类比较特殊,因为他是所有类的父类,是java类中最高层类。也就是说,在我们创建一个class时,如果没有指定该类的继承类,那么java.leng.Object类都是他们默认的继承类。
getClass()方法
该方法会返回某个对执行时的Class实例也就是对象,再通过Class实例调用getName()方法获取类的名称。
toString()方法
其返回值类型为String类型,返回类名和它的引用地址。
equals()方法
在Object类中是用来比较两个对象的引用地址是否相等。
7.3类的多态
多态在程序里面的意思是指 “一种定义,多种实现”。
java里的 “+”,作用于数的相加、求和,还有就是字符串连接符。类的多态性可以从两个方面体现:一是方法的重载,二是类的上下转型。
1.方法的重载
构造方法的名称由类名决定,构造方法只有一个名称。如果以不同的方式创建类的对象,那么就需要使用多个形参不同的构造方法来完成。在类中如果有多个构造方法存在,那么这一些构造方法的形参个数不一相同,而且必须且到方法“方法的重载”。
方法的重载就是在同一个类中允许同时存在多个同名方法,只要这些方法的参数个数或者类型不同就可以。
public class OverLoadTest {
// 定义一个方法
public static int add(int a) {
return a;
}
// 定义与第一个方法参数个数不同的方法
public static int add(int a, int b) {
return a + b;
}
// 定义与第一个方法相同名称、参数类型不同的方法
public static double add(double a, double b) {
return a + b;
}
// 定义一个成员方法
public static int add(int a, double b) {
return (int) (a + b);
}
// 这个方法与前一个方法参数次序不同
public static int add(double a, int b) {
return (int) (a + b);
}
// 定义不定长参数
public static int add(int... a) {
int s = 0;
// 根据参数个数循环操作
for (int i = 0; i < a.length; i++) {
s += a[i];// 将每个参数的值相加
}
return s;// 将计算结果返回
}
public static void main(String args[]) {
System.out.println("调用add(int)方法:" + add(1));
System.out.println("调用add(int,int)方法:" + add(1, 2));
System.out.println("调用add(double,double)方法:" + add(2.1, 3.3));
System.out.println("调用add(int a, double b)方法:" + add(1, 3.3));
System.out.println("调用add(double a, int b) 方法:" + add(2.1, 3));
System.out.println("调用add(int... a)不定长参数方法:"+ add(1, 2, 3, 4, 5, 6, 7, 8, 9));
System.out.println("调用add(int... a)不定长参数方法:" + add(2, 3, 4));
}
}
结果
2.向上转型
子类引用的对象转化为父类类型称为向上转型。通俗地来说就是将子类对象转为父类对象。此处父类对象也可以是接口。
在向上转型时,父类的对象无法调用子类独有的属性或者方法。而我们父类对象调用自己的属性或者方法是可以的。这其实就是向上转型的一个特性,向上转型的对象会遗失子类中父类没有的方法,而且子类的同名方法会覆盖父类的同名方法。相当于向上转型时,改对象对于只存在子类中而父类中不存在的方法是不能访问的。同时,若子类重写了父类的某些方法,在调用这些方法时,实际上调用的是子类定义的方法,这也就是动态链接、动态调用
3.向下转型
向下转型就是指父类类型的对象转型为子类类型。也就是,声明的是子类类型,但引用的是父类类型的对象。
同时向上转型可以由java编译器自动来完成的,但是向下转型就要人工强制转换
class Quadrangle {
public static void draw(Quadrangle q) {
// SomeSentence
}
}
public class Parallelogram extends Quadrangle {
public static void main(String args[]) {
draw(new Parallelogram());
// 将平行四边形类对象看作是四边形对象,称为向上转型操作
Quadrangle q = new Parallelogram();
Parallelogram p = q; // 将父类对象赋予子类对象
// //将父类对象赋予子类对象,并强制转换为子类型
//Parallelogram p = (Parallelogram) q;
}
}
4.instanceof 关键字
只有存在继承关系,才能使用instanceof关键字,否则会报错。
7.4 抽象类与接口
1.抽象类与抽象方法
抽象类简单来说就是表征一个对象是什么一种属性。实例化即建立一个类的对象,而所谓抽象是指它之下没有可以实例化的对象。
抽象类不能实例化,静态方法不需要实例化,将Java的这两个特性相结合有助于理解本段代码。
创建Market抽象类
分别 定义 TaobaoMarket 类 和 WallMarket 类,继承自 Market 类。
定义GoShopping类,实现TaobaoMarket 子类 和 WallMarket 子类创建的对象。
/**
* 使用抽象类模拟“去商场买衣服”的案例,然后通过派生类确定到底去哪个商场买衣服,买什么样的衣服。
*/
public class GoShopping {
public static void main(String[] args) {
Market market = new WallMarket();// 使用派生类对象创建抽象类对象
market.name = "沃尔玛";
market.goods = "七匹狼西服";
market.shop();
market = new TaobaoMarket();// 使用派生类对象创建抽象类对象
market.name = "淘宝";
market.goods = "韩都衣舍花裙";
market.shop();
}
}
只有声明没有实例的方法称为抽象方法,抽象方法只能在抽象类中使用,不然会报错。
2.接口的声明及实现
接口都没有方法实现,并且抽象程度更高。
定义接口:
//定义平行四边形类,该类实现了drawTest接口
class ParallelogramgleUseInterface implements drawTest {
public void draw() { // 由于该类实现了接口,所以需要覆盖draw()方法
System.out.println("平行四边形.draw()");
}
}
//定义正方形类,该类实现了drawTest接口
class SquareUseInterface implements drawTest {
public void draw() {
System.out.println("正方形.draw()");
}
}
public class QuadrangleUseInterface {
public static void main(String[] args) {
drawTest[] d = { // 接口也可以进行向上转型操作
new SquareUseInterface(), new ParallelogramgleUseInterface() };
for (int i = 0; i < d.length; i++) {
d[i].draw(); // 调用draw()方法
}
}
}
结果
3.多重继承
多重继承指的是一个类可以同时从多于一个的父类那里继承行为和特征,然而我们知道Java为了保证数据安全,它只允许单继承。
Java提供了两种方式实现多重继承:接口和内部类。
分别定义IFather接口和IMather接口:
public interface IFather {// 定义一个接口
void smoking();// 抽烟的方法
void goFishing();// 钓鱼的方法
}
public interface IMather {// 定义一个接口
void watchTV();// 看电视的方法
void cooking();// 做饭的方法
}
定义一个类,继承接口并实现方法:
public class Me implements IFather, IMather {// 继承IFather接口和IMather接口
@Override
public void watchTV() {// 重写watchTV()方法
System.out.println("我喜欢看电视");
}
@Override
public void cooking() {// 重写cook()方法
System.out.println("我喜欢做饭");
}
@Override
public void smoking() {// 重写smoke()方法
System.out.println("我喜欢抽烟");
}
@Override
public void goFishing() {// 重写goFishing()方法
System.out.println("我喜欢钓鱼");
}
public static void main(String[] args) {
IFather father = new Me();// 通过子类创建IFather接口对象
System.out.println("爸爸的爱好:");
// 使用接口对象调用子类中实现的方法
father.smoking();
father.goFishing();
IMather mather = new Me();// 通过子类创建IMather接口对象
System.out.println("\n妈妈的爱好:");
// 使用接口对象调用子类中实现的方法
mather.cooking();
mather.watchTV();
}
}
7.5 访问控制
1.访问控制符
访问控制符 | 当前类 | 同一包类 | 子孙类 | 其他包 |
public | Y | Y | Y | Y |
protected | Y | Y | Y | N |
default | Y | Y | N | N |
private | Y | N | N | N |
2.Java类包
当我们开发项⽬的时候,会⽤到很多的类文件,这样就不⽅便管理
我们可以通过包对类进⾏管理分类,也可以避免重名的问题,⼀种类型的java类放在⼀个包⾥⾯,或者⼀个模块的java类放在⼀个包⾥⾯,不同包⾥⾯可以包含同名的类,通过包类区分。
3.final关键字
final类,final方法,final变量
final类,不能被扩展(或子类化)。
final class FinalClass {
int a = 3;
void doit() {
}
public static void main(String args[]) {
FinalClass f = new FinalClass();
f.a++;
System.out.println(f.a);
}
}
final方法,不能在包含该方法的类的子类中重新定义(覆盖或隐藏)
class Parents {
private final void doit() {
System.out.println("父类.doit()");
}
final void doit2() {
System.out.println("父类.doit2()");
}
public void doit3() {
System.out.println("父类.doit3()");
}
}
class Sub extends Parents {
public final void doit() { // 在子类中定义一个doit()方法
System.out.println("子类.doit()");
}
// final void doit2(){ //final方法不能覆盖
// System.out.println("子类.doit2()");
// }
public void doit3() {
System.out.println("子类.doit3()");
}
}
public class FinalMethod {
public static void main(String[] args) {
Sub s = new Sub(); // 实例化
s.doit(); // 调用doit()方法
Parents p = s; // 执行向上转型操作
// p.doit(); //不能调用private方法
p.doit2();
p.doit3();
}
}
j结果
final变量,只能被赋值一次。最终变量的值在设置后不能修改。
import static java.lang.System.out;
import java.util.Random;
class Test {
int i = 0;
}
public class FinalData {
static Random rand = new Random();
private final int VALUE_1 = 9; // 声明一个final常量
private static final int VALUE_2 = 10; // 声明一个final、static常量
private final Test test = new Test(); // 声明一个final引用
private Test test2 = new Test(); // 声明一个不是final的引用
private final int[] a = { 1, 2, 3, 4, 5, 6 }; // 声明一个定义为final的数组
private final int i4 = rand.nextInt(20);
private static final int i5 = rand.nextInt(20);
public String toString() {
return i4 + " " + i5 + " ";
}
public static void main(String[] args) {
FinalData data = new FinalData();
//data.test = new Test();
// 可以对指定为final的引用中的成员变量赋值
// 但不能将定义为final的引用指向其他引用
//data.value2++;
// 不能改变定义为final的常量值
data.test2 = new Test(); // 可以将没有定义为final的引用指向其他引用
for (int i = 0; i < data.a.length; i++) {
// a[i]=9;
// 不能对定义为final的数组赋值
}
out.println(data);
out.println("data2");
out.println(new FinalData());
out.println(data);
}
}
结果
import java.util.Random;
import static java.lang.System.out;
public class FinalStaticData {
private static Random rand = new Random(); // 实例化一个Random类对象
// 随机产生0~10之间的随机数赋予定义为final的a1
private final int a1 = rand.nextInt(10);
// 随机产生0~10之间的随机数赋予定义为static final的a2
private static final int a2 = rand.nextInt(10);
public static void main(String[] args) {
FinalStaticData fdata = new FinalStaticData(); // 实例化一个对象
// 调用定义为final的a1
out.println("重新实例化对象调用a1的值:" + fdata.a1);
// 调用定义为static final的a2
out.println("重新实例化对象调用a1的值:" + fdata.a2);
// 实例化另外一个对象
FinalStaticData fdata2 = new FinalStaticData();
out.println("重新实例化对象调用a1的值:" + fdata2.a1);
out.println("重新实例化对象调用a2的值:" + fdata2.a2);
}
}
结果
7.6 内部类
1.成员内部类
成员内部类看起来像是外部类的一个成员,所以内部类可以拥有private、public等访问权限修饰。也可以用static来修饰。成员内部类分为:
静态成员内部类:使用static修饰类;
非静态成员内部类:未用static修饰类,在没有说明是静态成员内部类时,默认成员内部类指的就是非静态成员内部类;
public class OuterClass {
innerClass in = new innerClass(); // 在外部类实例化内部类对象引用
public void ouf() {
in.inf(); // 在外部类方法中调用内部类方法
}
class innerClass {
innerClass() { // 内部类构造方法
}
public void inf() { // 内部类成员方法
}
int y = 0; // 定义内部类成员变量
}
public innerClass doit() { // 外部类方法,返回值为内部类引用
// y=4; //外部类不可以直接访问内部类成员变量
in.y = 4;
return new innerClass(); // 返回内部类引用
}
public static void main(String args[]) {
OuterClass out = new OuterClass();
// 内部类的对象实例化操作必须在外部类或外部类的非静态方法中实现
OuterClass.innerClass in = out.doit();
OuterClass.innerClass in2 = out.new innerClass();
}
}
注:只有成员内部类才能加static变成静态内部类
interface OutInterface { // 定义一个接口
public void f();
}
public class InterfaceInner {
public static void main(String args[]) {
OuterClass2 out = new OuterClass2 (); // 实例化一个OuterClass2对象
// 调用doit()方法,返回一个OutInterface接口
OutInterface outinter = out.doit();
outinter.f(); // 调用f()方法
}
}
class OuterClass2 {
// 定义一个内部类实现OutInterface接口
private class InnerClass implements OutInterface {
InnerClass(String s) { // 内部类构造方法
System.out.println(s);
}
public void f() { // 实现接口中的f()方法
System.out.println("访问内部类中的f()方法");
}
}
public OutInterface doit() { // 定义一个方法,返回值类型为OutInterface接口
return new InnerClass("访问内部类构造方法");
}
}
结果
2.局部内部类
局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。
interface OutInterface2 { // 定义一个接口
}
class OuterClass3 {
public OutInterface2 doit(final String x) { // doit()方法参数为final类型
// 在doit()方法中定义一个内部类
class InnerClass2 implements OutInterface2 {
InnerClass2(String s) {
s = x;
System.out.println(s);
}
}
return new InnerClass2("doit");
}
}
3.匿名内部类
匿名内部类是唯一一种没有构造器的类。正因为其没有构造器,所以匿名内部类的使用范围非常有限,大部分匿名内部类用于接口回调。一般来说,匿名内部类用于继承其他类或是实现接口,并不需要增加额外的方法,只是对继承方法的实现或是重写。
interface OutInterface2 { // 定义一个接口
}
class OuterClass4 {
public OutInterface2 doit() { // 定义doit()方法
return new OutInterface2() { // 声明匿名内部类
private int i = 0;
public int getValue() {
return i;
}
};
}
}
4.静态内部类
静态内部类可以使用 static 关键字定义,静态内部类我们不需要创建外部类来访问,可以直接访问它。
public class StaticInnerClass {
int x = 100;
static class Inner {
void doitInner() {
// System.out.println("外部类"+x);
}
public static void main(String args[]) {
System.out.println();
}
}
}
5.内部类的继承
内部类和其他普通类一样,同样可以被继承,这样给本来就十分灵活的内部类增加了更好的结构性和代码复用性。只是内部类的继承和普通类有一些不同之处,是在使用时需要多加注意的。因为内部类在创建时需要外部类的引用,所以在内部类的继承上也需要外部类的协助