目录
一、封装(Encapsulation)
封装是把过程和数据包围起来,对数据的访问只能通过已定义的接口。面向对象计算始于这个基本概念,即现实世界可以被描绘成一系列完全自治、封装的对象,这些对象通过一个受保护的接口访问其他对象。封装是一种信息隐藏技术,在java中通过关键字private, protected和public实现封装。什么是封装?封装把对象的所有组成部分组合在一起,封装定义程序如何引用对象的数据,封装实际上使用方法将类的数据隐藏起来,控制用户对类的修改和访问数据的程度。适当的封装可以让程式码更容易理解和维护,也加强了程式码的安全性。
程序设计追求“高内聚,低耦合”:
- 高内聚:类的内部数据操作细节自己完成,不允许外部干涉;
- 低耦合:仅对外暴露少量的方法用于使用。
隐藏对象内部的复杂性,只对外公开简单的接口。便于外界调用,从而提高系统的可扩展性、可维护性,提高代码的安全性。通俗的说,把该隐藏的隐藏起来,该暴露的暴露出来。这就是封装性的设计思想。
public class Girl {//女孩
//属性:
private int age;
//读取年龄:
public int duquAge(){
return age;
}
//设置年龄:
public void shezhiAge(int age){
if(age >= 30 ){
this.age = 18;
}else{
this.age = age;
}
}
}
public class Test {
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
//创建一个Girl类的对象:
Girl g = new Girl();
/*g.age = 33;
System.out.println(g.age);*/
//设置年龄:
g.shezhiAge(31);
//读取年龄:
System.out.println(g.duquAge());
}
}
上面的代码,对于属性age来说,我加了修饰符private,这样外界对它的访问就受到了限制,现在我还想加上其他的限制条件,但是在属性本身上没有办法再加了,所以我们通过定义方法来进行限制条件的添加。
以属性为案例:进行封装:
- 将属性私有化,被private修饰 —> 加入权限修饰符,一旦加入了权限修饰符,其他人就不可以随意的获取这个属性
- 提供public修饰的方法让别人来访问/使用
- 即使外界可以通过方法来访问属性了,但是也不能随意访问,因为咱们在方法中可以加入限制条件。
实际开发中,方法一般会写成 setter,getter方法:
可以利用IDEA快捷键生成:alt+insert —> getter and setter:
public class Girl {//女孩
//属性:
private int age;
//读取年龄:
public int getAge(){
return age;
}
//设置年龄:
public void setAge(int age){
if(age >= 30 ){
this.age = 18;
}else{
this.age = age;
}
}
}
public class Student {
//属性:
private int age;
private String name;
private String sex;
//加入对应的setter和getter方法:
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
if("男".equals(sex) || "女".equals(sex) ){//sex是男 或者 是 女
this.sex = sex;
}else{
this.sex = "男";
}
}
//加入构造器:
public Student(){
}
public Student(int age,String name,String sex){
this.age = age;
this.name = name;
//this.sex = sex;
this.setSex(sex);
}
}
public class Test {
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
//创建一个Student对象:
Student s1 = new Student();
s1.setName("nana");
s1.setAge(19);
s1.setSex("女");
System.out.println(s1.getName()+"---"+s1.getAge()+"----"+s1.getSex());
Student s2 = new Student(18,"菲菲","asdfasdfsadf");
System.out.println(s2.getName()+"---"+s2.getAge()+"----"+s2.getSex());
}
}
二、继承(Inheritance)
1. 继承
类是对对象的抽象;继承是对类的抽象。
继承关系,是在合理的范围中进行的抽取 ,抽取出子类父类的关系(继承 就是 is - a 的关系) :
- 员工是一个人 —> 合理
- 学生是一个狗 —> 不合理
public class Person {
//属性:
private int age;
private String name;
private double height;
//提供setter getter方法:
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
//方法:
public void eat(){
System.out.println("可以吃饭。。。");
}
public void sleep(){
System.out.println("可以睡觉。。。");
}
}
public class Student extends Person {//子类Student 继承 父类Person
//属性:
private int sno;//学号
public int getSno() {
return sno;
}
public void setSno(int sno) {
this.sno = sno;
}
//方法:
public void study(){
System.out.println("学生可以学习");
}
}
public class Test {
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
//创建子类Student的对象
Student s = new Student();
s.setSno(1001);
s.setAge(18);
s.setName("菲菲");
s.setHeight(180.4);
System.out.println("学生名字为:"+s.getName()+",学生的年纪:"+s.getAge());
//访问方法:
s.study();
s.eat();
s.sleep();
}
}
总结:
- 继承提高了代码的复用性,父类定义的内容,子类可以直接拿过来用就可以了,不用代码上重复定义了。便于代码的扩展。
- 为了以后多态的使用。是多态的前提。
- 父类private修饰的内容,子类实际上也继承,只是因为封装的特性阻碍了直接调用,但是提供了间接调用的方式,可以间接调用。
- 一个父类可以有多个子类,一个子类只能有一个直接父类
- 继承具有传递性:Student —> 继承自 Person —> 继承自Object
Object类是所有类的根基父类。所有的类都直接或者间接的继承自Object。
继承的内存分析:
2. 权限修饰符
同一个类 | 同一个包 | 子类 | 所有类 | |
---|---|---|---|---|
private | * | |||
default | * | * | ||
protected | * | * | * | |
publlic | * | * | * | * |
一般属性:用private修饰
方法:用public修饰
3. 方法的重写
重写:发生在子类和父类中,当子类对父类提供的方法不满意的时候,要对父类的方法进行重写。
子类的方法名字和父类必须一致,参数列表(个数,类型,顺序)也要和父类一致。
public class Person {
public void eat(){
System.out.println("吃食物");
}
public void sleep(){
System.out.println("睡觉");
}
}
public class Student extends Person {
public void study(){
System.out.println("学习");
}
@override
public void eat(){
System.out.println("我喜欢吃小龙虾喝啤酒。。");
}
}
public class Test {
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
//创建一个Student类的对象:
Student s = new Student();
s.eat();
}
}
内存分析:
重载和重写的区别:
- 重载:在同一个类中,当方法名相同,形参列表不同的时候 多个方法构成了重载
- 重写:在不同的类中,子类对父类提供的方法不满意的时候,要对父类的方法进行重写。
英文 | 位置 | 修饰符 | 返回值 | 方法名 | 参数 | 抛出异常 | 方法体 | |
---|---|---|---|---|---|---|---|---|
重载 | overload | 同一个类中 | 无关 | 无关 | 必须相同 | 必须不同 | 无关 | 不同 |
重写 | override | 子类父类中 | 无关 | 父类的权限修饰符要低于子类的 | 父类的返回值类型大于子类 | 必须相同 | 必须相同 | 小于等于 |
4. super
super指的父类的。super可以修饰属性,可以修饰方法。
- 在子类的方法中,可以通过 super.属性、super.方法 的方式,显示地去调用父类提供的属性、方法。在通常情况下,super. 可以省略不写
- 在特殊情况下,当子类和父类的属性或方法重名时,你要想使用父类的属性或方法,必须加上修饰符 super. ,只能通过 super. 方法来调用
super修饰构造器:
- 其实我们平时写的构造器的第一行都有:super() —> 作用:调用父类的空构造器,只是我们一般都省略不写。
- 如果构造器中已经显示的调用super父类构造器,那么它的第一行就没有默认分配的super();了
- 在构造器中,super调用父类构造器和this调用子类构造器只能存在一个,两者不能共存:因为super修饰构造器要放在第一行,this修饰构造器也要放在第一行:
继承条件下构造方法的执行过程: 一层一层super
5. object类
所有类都直接或间接的继承自Object类,Object类是所有Java类的根基类。
也就意味着所有的Java对象都拥有Object类的属性和方法。
如果在类的声明中未使用extends关键字指明其父类,则默认继承Object类。
5.1 toString()
但使用toString()方法的时候,打印出来的东西 “不好看”,对于其他人来说不友好,可读性不好。我们现在是想知道对象的信息,名字,年龄,身高。
现在的格式不好:
出现的问题:子类Student对父类Object提供的toString()方法不满意,不满意 —> 对toString方法进行重写:
public class Student /*extends Object*/{
private String name;
private int age;
private double height;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
public Student() {
}
public Student(String name, int age, double height) {
this.name = name;
this.age = age;
this.height = height;
}
//重写toString()
public String toString() {
return "这是一个Student对象,这个对象的名字:"+name+",年龄:"+age+",身高:"+height;
}
}
IDEA提供了重写toString()快捷键: Alt+Insert
/*public String toString() {
return "这是一个Student对象,这个对象的名字:"+name+",年龄:"+age+",身高:"+height;
}*/
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", height=" + height +
'}';
}
}
5.2 equals()
equals作用:这个方法提供了对对象的内容是否相等 的一个比较方式,对象的内容指的就是属性。
boolean flag = p1.equals(p2);
父类Object提供的equals就是在比较==地址,没有实际的意义,我们一般不会直接使用父类提供的方法,而是在子类中对这个方法进行重写。
- Eclipse
- IDEA
6.类的关系
类和类可以产生关系:
将一个类作为另一个类中的方法的形参;将一个类作为另一个类的属性。
public class Girl {
//属性:
String name;
double weight;
Mom m /*= new Mom()*/;
//方法:
public void add(int a){//参数是基本数据类型
System.out.println(a);
System.out.println(a+100);
}
//谈恋爱的方法:
public void love(Boy b){//参数是引用数据类型Boy
System.out.println("我男朋友的名字是:"+b.name+",我男朋友的年龄是:"+b.age);
b.buy();
}
//女孩跟妈妈聊天:
public void wechat(){
m.say();
}
//构造器:
public Girl(String name, double weight) {
this.name = name;
this.weight = weight;
}
}
public class Boy {
//属性:
int age;
String name;
//方法:
public void buy(){
System.out.println("跟我谈恋爱,我给你买买买。。。");
}
//构造器:
public Boy(int age, String name) {
this.age = age;
this.name = name;
}
}
public class Mom {
//方法:
public void say(){
System.out.println("妈妈唠唠叨叨 都是爱,听妈妈的话。。");
}
}
public class Test {
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
//创建一个Boy类的具体的对象:
Boy boy = new Boy(30,"鹿晗");
//创建一个Girl类的具体的对象:
Girl girl = new Girl("关晓彤",100);
//谈恋爱:
//girl.love(boy);
Boy boy2 = new Boy(35,"陈伟霆");
girl.love(boy2);
//还可以跟妈妈微信聊天:
girl.m = new Mom();
girl.wechat();
}
}
三、多态(polymorphism)
1. 多态
public class Animal {//父类:动物:
public void shout(){
System.out.println("我是小动物,我可以叫。。。");
}
}
public class Cat extends Animal{
//喊叫方法:
public void shout(){
System.out.println("我是小猫,可以喵喵叫");
}
public void scratch(){
System.out.println("我是小猫,我可以挠人");
}
}
public class Dog extends Animal{
//喊叫:
public void shout(){
System.out.println("我是小狗,我可以汪汪叫");
}
public void guard(){
System.out.println("我是小狗,我可以看家护院,保护我的小主人。。。");
}
}
public class Pig extends Animal{
public void shout(){
System.out.println("我是小猪,我嗯嗯嗯的叫");
}
public void eat(){
System.out.println("我是小猪,我爱吃东西。。");
}
}
public class Girl {
//跟猫玩耍:
/*public void play(Cat cat){
cat.shout();
}*/
//跟狗玩耍:
/*public void play(Dog dog){
dog.shout();
}*/
//跟小动物玩耍:
public void play(Animal an){
an.shout();
}
}
public class Test {
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
//具体的猫:--》猫的对象
//Cat c = new Cat();
//具体的小女孩:--》女孩的对象
Girl g = new Girl();
//小女孩跟猫玩:
//g.play(c);
//具体的狗---》狗的对象:
//Dog d = new Dog();
//小女孩跟狗玩:
//g.play(d);
//具体的动物:--》动物的对象:
//Cat c = new Cat();
//Dog d = new Dog();
Pig p = new Pig();
Animal an = p;
g.play(an);
}
}
总结:
- 多态跟属性无关,多态指的是方法的多态,而不是属性的多态。
- 先有父类,再有子类 —> 继承 ;先有子类,再抽取父类 —> 泛化
- 什么是多态:
多态就是多种状态:同一个行为,不同的子类表现出来不同的形态。
多态指的就是同一个方法调用,然后由于对象不同会产生不同的行为。 - 多态的要素:
一、继承: Cat extends Animal ,Pig extends Animal, Dog extends Animal
二、重写:子类对父类的方法shout()重写
三、父类引用指向子类对象:
Pig p = new Pig();
Animal an = p;
//合为一句话
Animal an = new Pig();
Animal an = new Pig();
左侧:编译期的类型;右侧:运行期的类型
public void play(Animal an){//Animal an = an = new Pig();
an.shout();
}
上面的代码,也是多态的一种非常常见的应用场合:父类当方法的形参,然后传入的是具体的子类的对象,然后调用同一个方法,根据传入的子类的不同展现出来的效果也不同,构成了多态。
内存分析:
2. 向下转型,向上转型
访问到eat()方法和weight属性需要转型:
public class Demo {
//这是一个main方法,是程序的入口:
public static void main(String[] args) {
Pig p = new Pig();
Animal an = p;//转型:向上转型
an.shout();
//加入转型的代码:
//将Animal转为Pig类型:
Pig pig = (Pig)an ;//转型:向下转型
pig.eat();
pig.age = 10;
pig.weight = 60.8;
}
}
对应内存:
思考之前的equals方法:判断是否是同一个类与转型不冲突
3. 简单工厂设计模式
不仅可以使用父类做方法的形参,还可以使用父类做方法的返回值类型,真实返回的对象可以是该类的任意一个子类对象。
简单工厂模式的实现,它是解决大量对象创建问题的一个解决方案。将创建和使用分开,工厂负责创建,使用者直接调用即可。简单工厂模式的基本要求是:
- 定义一个static方法,通过类名直接调用
- 返回值类型是父类类型,返回的可以是其任意子类类型
- 传入一个字符串类型的参数,工厂根据参数创建对应的子类产品
public class Test {
public static void main(String[] args) {
Girl g = new Girl();
//Cat c = new Cat();
//Dog d = new Dog();
//Pig p = new Pig();
Animal an = PetStore.getAnimal("狗");
g.play(an);
}
}
public class PetStore {//宠物店 ---》工厂类
//方法:提供动物
public static Animal getAnimal(String petName){//多态的应用场合(二)
Animal an = null;
if("猫".equals(petName)){//petName.equals("猫") --》这样写容易发生空指针异常
an = new Cat();
}
if("狗".equals(petName)){
an = new Dog();
}
if("猪".equals(petName)){
an = new Pig();
}
return an;
}
}