一、IDEA使用
二、包
三、访问修饰符
四、OOP三大特征(封装、继承和多态)
面向对象编程有三大特征:封装、继承和多态。
1.封装
(1)基本介绍
封装(encapsulation)就是把抽象出的数据(属性)和对数据的操作(方法)封装在一起,数据被保护在内部,程序的其他部分只有通过被授权的操作(方法),才能对数据进行操作。
对电视机的操作就是典型的封装。
好处:
1)隐藏实现细节
2)可以对数据进行验证,保证安全合理
(2)实现步骤(三步)
1)将属性进行私有化——private(使不能直接修改属性)
2)提供一个公共的(public)set方法,用于对属性判断并赋值
public void setXXX(类型 参数名){//XXX表示某个属性
//加入数据验证的业务逻辑
属性=参数名;
}
3)提供一个公共的(public)get方法,用于获取属性的值
public 数据类型 getXXX(){//权限判断,XXX某个属性
return XXX;
}
(3)快速入门
完成这样一个小程序:不能随便查看人的年龄、工资等隐私,并对设置的年龄进行合理的验证。年龄合理就设置,否则给默认年龄。年龄必须在1-120,年龄、工资不能直接查看,name的长度在2-6之间。
将构造器和setXXX结合:将set方法写进构造器,可以在使用构造器时也能验证信息。
package com.hspedu.encap;
public class Encapsulation01 {
public static void main(String[] args) {
//如果要使用快捷键alt+r,需要先配置主类
//第一次,我们使用鼠标点击形式运算程序,后面就可以用
Person person = new Person();
person.setName("jackj");
person.setAge(30);
person.setSalary(30000);
System.out.println(person.info());
System.out.println(person.getSalary());
//如果使用构造器指定属性->设置的setXXX失效,无法对信息进行校验->将setXXX写进构造器
Person smith = new Person("smith", 20, 50000);
System.out.println("=====smith=======");
System.out.println(smith.info());
}
}
class Person {
public String name;//名字公开
private int age;//年龄私有化
private double salary;//工资私有化
//无参构造器
public Person() {
}
//有3个参数的构造器
public Person(String name, int age, double salary) {
//this.name = name;
//this.age = age;
//this.salary = salary;
//可以将set方法写在构造器中,这样仍然可以验证数据
setName(name);
setAge(age);
setSalary(salary);
}
//setXXX和getXXX快捷键:alt+insert
public String getName() {
return name;
}
public void setName(String name) {
//加入对数据的校验
if(name.length() >= 2 && name.length() <=6) {
this.name = name;
} else {
System.out.println("你设置的姓名长度不对,需要在2-6个字符之间,默认名字:无名");
this.name = "无名";
}
}
public int getAge() {
return age;
}
public void setAge(int age) {
//判断
if(age >= 1 && age <= 120) {
this.age = age;
} else {
System.out.println("你设置的年龄不对,需要在1-120岁之间,给默认年龄18");
this.age = 18;
}
}
public double getSalary() {
//可以增加对当前对象的权限判断
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public String info() {
return "信息为 name=" + name + " age=" + age + " 薪水=" + salary;
}
}
(4)课堂练习
com.hspedu.encap:AccountTest.java和Account.java
创建程序:在其中定义两个类:Account和AccountTest类体会Java的封装性。
1.Account类要求具有属性:姓名(长度为2位3位或4位)、余额(必须>20)、密码(必须是6位),如果不满足,则给出提示信息,并给默认值。
2.通过setXXX的方法给Account的属性赋值。
3.在AccountTest中测试
package com.hspedu.encap;
public class AccountTest {
public static void main(String[] args) {
Account account = new Account("xiaolin", 99.9, "8907789");
System.out.println("====账户信息如下=====");
System.out.println(account.info());
}
}
package com.hspedu.encap;
public class Account {
private String name;
private double balance;
private String password;
public Account(String name, double balance, String password) {
setName(name);
setBalance(balance);
setPassword(password);
}
public String getName() {
return name;
}
public void setName(String name) {
if (name.length() >=2 && name.length() <= 4) {
this.name = name;
} else {
System.out.println("姓名长度错误,应为2位、3位或4位");
this.name = "无名";
}
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
if(balance > 20) {
this.balance = balance;
} else {
System.out.println("余额必须大于20");
this.balance = 0000000000;
}
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
if(password.length() == 6) {
this.password = password;
} else {
System.out.println("密码应设置为6位");
this.password = "000000";
}
}
public String info() {
return "姓名:" + this.name + " 余额:" + this.balance + " 密码:" + this.password;
}
}
2.继承
(1)基本介绍
继承可以解决代码复用,让我们的编程更加靠近人类思维。当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过extends来声明继承父类即可。
(2)基本语法
class 子类 extends 父类 {
}
1)子类就会自动拥有父类定义的属性和方法。
2)父类又叫超类、基类。
3)子类又叫派生类。
(3)快速入门
com.hspedu.extend_.improve_:Student.java
package com.hspedu.extend_.improve_;
//父类,是Pupil和Graduate的父类
public class Student {
//共有属性
public String name;
public int age;
private double score;
//共有的方法
//setScore:设置分数
public void setScore(double score) {
this.score = score;
}
//显示学生信息
public void showInfo() {
System.out.println("学生名:" + this.name + " 年龄:" + this.age + " 分数:" + score);
}
}
com.hspedu.extend_.improve_:Graduate.java
package com.hspedu.extend_.improve_;
//让Graduate继承Students类
public class Graduate extends Student{
//testing:大学生考试状态
public void testing() {
System.out.println("大学生" + this.name + "正在考小学数学...");
}
}
com.hspedu.extend_.improve_:Pupil.java
package com.hspedu.extend_.improve_;
//让Pupil继承Student类
public class Pupil extends Student{
//testing:小学生考试状态
public void testing() {
System.out.println("小学生" + this.name + "正在考小学数学...");
}
}
com.hspedu.extend_.Pupil:Extends01.java
package com.hspedu.extend_.improve_;
import com.hspedu.extend_.Graduate;
import com.hspedu.extend_.Pupil;
public class Extends01 {
public static void main(String[] args) {
com.hspedu.extend_.Pupil pupil = new Pupil();
pupil.name = "金角大王~";
pupil.age = 10;
pupil.testing();
pupil.setScore(80);
pupil.showInfo();
System.out.println("=================================");
com.hspedu.extend_.Graduate graduate = new Graduate();
graduate.name = "银角大王~";
graduate.age = 23;
graduate.testing();
graduate.setScore(99);
graduate.showInfo();
}
}
(4)继承给编程带来的便利
1)代码的复用性提高了
2)代码的扩展性和维护性提高了
(5)细节
com.hspedu.extend_:Base.java(父类) Sub.java(子类) ExtendsDetail.java(主方法)
1)子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问,但是私有的属性和方法不能在子类直接访问,要通过父类提供公共的方法去访问。
2)子类必须调用父类的构造器,完成父类的初始化。
3)当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器。如果父类没有提供无参构造器,则必须在子类的构造器中用super去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过。
4)如果希望指定去调用父类的某个构造器,则显式地调用一下:super(参数列表)。
5)super在使用时,必须放在构造器第一行。(super只能在构造器中使用)
(先执行父类的构造器,再执行子类的构造器)
6)super()和this()都只能放在构造器第一行,因此这两个方法不能共存在一个构造器。
如果构造器中有了this(),“默认调用父类的无参构造器”将失效。
7)java所有类都是Object类的子类,即Object是所有类的父类。
ctrl+H可以查看类的层级关系。
8)父类构造器的调用不限于直接父类,将一直往上追溯直到Object类(顶级父类)。
9)子类最多只能继承一个父类(指直接继承),即java中是单继承机制。
思考:如何让A类继承B类和C类?【A继承B,B继承C】
10)不能滥用继承,子类和父类之间必须满足is-a的逻辑关系。
例如:Person is a Music? Music extends Person;//不合理
Cat is an Animal. Cat extends Animal;//合理
(6)继承的本质分析
案例:
我们看一个案例来分析当子类继承父类,创建子类对象时,内存中到底发生了什么?
答:当子类对象创建好后,建立查找的关系。
子类创建的内存布局
流程:
1.先加载类信息(先加载父类,后加载子类)。
加载顺序:Object→GrandPa→Father→Son。
2.在堆中开辟空间。
....
package com.hspedu.extend_;
/**
* 讲解继承的本质
*/
public class ExtendsTheory {
public static void main(String[] args) {
Son son = new Son();//内存的布局?
//这是请大家注意,要按照查找关系来返回信息
//(1)首先看子类是否有该属性
//(2)如果子类有这个属性,并且可以访问,则返回信息。(如果有,但不可以访问,则报错)
//(3)如果子类没有这个属性,就看父类有没有这个属性(如果父类有该属性,并且可以访问,就返回信息...)
//(4)如果父类没有,就按照(3)的规则,继续找上级父类,直到Object...
System.out.println(son.name);//大头儿子
// System.out.println(son.age);//报错,因为父类中age是私有的,查找线路被堵住,爷类中的age是共有的却不会被访问到
System.out.println(son.getAge());
System.out.println(son.habby);//旅游
}
}
class GrandPa {//爷类
//成员变量(属性)
String name = "大头爷爷";
String habby = "旅游";
int age = 80;
}
class Father extends GrandPa {//父类
//成员变量(属性)
String name = "大头爸爸";
private int age = 39;
public int getAge() {
return age;
}
}
class Son extends Father {//子类
String name = "大头儿子";
}
(7)课堂练习
练习一:main方法中:B b = new B(); 会输出什么?
package com.hspedu.extend_.exercise;
public class ExtendsExercise01 {
public static void main(String[] args) {
B b = new B();//a , b name , b
}
}
class A {
A() {
System.out.println("a");
}
A(String name) {
System.out.println("a name");
}
}
class B extends A {
B() {
this("abc");//因为有this(),故而不会调用A类的无参构造器
System.out.println("b");
}
B(String name) {
//默认调用A类的无参构造器
System.out.println("b name");
}
}
练习2:main方法中:C c = new C(); 输出什么内容?
package com.hspedu.extend_.exercise;
public class ExtendsExercise02 {
public static void main(String[] args) {
C c = new C();
}
}
class A {
public A() {
System.out.println("我是A类");
}
}
class B extends A {
public B() {
System.out.println("我是B类的无参构造");
}
public B(String name) {
System.out.println(name + "我是B类的有参构造");
}
}
class C extends B {
public C() {
this("hello");
System.out.println("我是C类的无参构造");
}
public C(String name) {
super("hahah");
System.out.println("我是C类的有参构造");
}
}
练习3:
编写Computer类,包含CPU、内存、硬盘等属性,getDetails方法用于返回Computer的详细信息;编写PC子类,继承Computer类,添加特有属性【品牌:brand】;编写NotePad子类,继承Computer类,添加特有属性【颜色:color】;编写ExtendsExercise类,在main方法中创建PC和NotePad对象,分别给对象中特有的属性赋值,以及给从Computer类继承的属性赋值,并使用方法并打印输出信息。
com.hspedu.extend_.exercise:Computer.java PC.java NotePad.java ExtendsExercise.java
package com.hspedu.extend_.exercise;
public class Computer {
//成员变量(属性)
private String cpu;
private int memory;
private int disk;
//构造器
public Computer(String cpu, int memory, int disk) {
this.cpu = cpu;
this.memory = memory;
this.disk = disk;
}
//成员方法
//getDetails:返回Computer信息
public String getDetails() {
return "CPU:" + this.cpu + " Memory:" + this.memory + " HardDisk:" + this.disk;
}
public String getCpu() {
return cpu;
}
public void setCpu(String cpu) {
this.cpu = cpu;
}
public int getMemory() {
return memory;
}
public void setMemory(int memory) {
this.memory = memory;
}
public int getDisk() {
return disk;
}
public void setDisk(int disk) {
this.disk = disk;
}
}
com.hspedu.extend_.exercise:PC.java
package com.hspedu.extend_.exercise;
public class PC extends Computer{
//成员变量(属性)
private String brand;
//构造器
//这里IDEA根据继承的规则,自动把构造器的调用写好
//这里也体现:继承设计的基本思想,父类的构造器完成父类的属性初始化
//子类的构造器完成子类的属性初始化
public PC(String cpu, int memory, int disk, String brand) {
super(cpu, memory, disk);
this.brand = brand;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public void printInfo() {
System.out.println("PC信息:");
// System.out.println(getCpu() + getMemory() + getDisk());
//调用父类的getDetails方法,得到相关属性信息
System.out.println(getDetails() + " brand:" + brand);
}
}
com.hspedu.extend_.exercise:NotePad.java
package com.hspedu.extend_.exercise;
public class NotePad extends Computer{
//特有属性
private String color;
//构造器
public NotePad(String cpu, int memory, int disk, String color) {
super(cpu, memory, disk);
this.color = color;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public void printInfo() {
System.out.println("NotePad的信息:");
System.out.println(getDetails() + " color:" + color);
}
}
com.hspedu.extend_.exercise:ExtendsExercise.java
package com.hspedu.extend_.exercise;
public class ExtendsExercise03 {
public static void main(String[] args) {
PC pc = new PC("Intel", 128,24,"联想");
pc.printInfo();
NotePad notePad = new NotePad("Apple", 128,128,"银色");
notePad.printInfo();
}
}
五、Super关键字
1.基本介绍
super代表父类的引用,用于访问父类的属性、方法和构造器。
2.基本语法
(1)访问父类的属性,但不能访问父类的private属性。
super.属性名;
(2)访问父类的方法,但不能访问父类的private方法。
super.方法名;
(3)访问父类的构造器。
super(参数列表);//只能放在构造器的第一句,只能出现一句!
com.hspedu.super_:A.java
package com.hspedu.super_;
public class A {
public int n1 = 100;//public 同类 同包 子类 不同包
protected int n2 = 200;//protected 同类 同包 子类
int n3 = 300;//默认 同类 同包
private int n4 = 400;//private 同类
public A() {}
public A(String name) {}
public A(String name, int age) {}
public void test100() {
}
protected void test200() {
}
void test300() {
}
private void test400() {
}
}
com.hspedu.super_:B.java
package com.hspedu.super_;
public class B extends A {
//访问父类的属性,但不能访问父类的private属性
public void hi() {
System.out.println(super.n1);
System.out.println(super.n2);
System.out.println(super.n2);
}
//访问父类的构造器:只能放在构造器地第一句,只能出现一句
public B() {
// super();//访问父类的无参构造器
// super("王一博");//访问父类的一个参数构造器
super("王一博", 27);//访问父类地两个参数构造器
}
//访问父类的方法,但不能访问父类的private方法
public void ok() {
super.test100();
super.test200();
super.test300();
}
}
3.使用细节
(1)调用父类的构造器的好处:分工明确,父类属性由父类初始化,子类属性由子类初始化。
(2)当子类中有和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须通过super。如果没有重名,使用super、this、直接访问是一样的效果。
(3)super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用super去访问爷爷类的成员;如果多个基类中都有同名的成员,使用super访问遵循就近原则。
//say()和this.say()
//(1)先找本类:如果有该方法,并且可以访问,则调用;如果没有,则查找父类
//(2)查找父类:如果父类中有该方法,并且可以访问,则调用;如果没有,则查找父类的父类...直到Object类
//提示1:如果有该方法,但却是私有的,则报错
//提示2:如果查找到Object类,却仍旧没有找到该方法,则提示没有该方法
say();
this.say();
//super.say()
//直接查找父类中是否有该方法
super.say();