Java编程思想_面向对象之封装
面向对象思想概述
面向对象思想:
封装,继承,多态
面向对象思想就是把关注点放在一件事或一个活动中涉及到的人或事物(也就是对象)上的思想(或思维方式)
面向对象关键字:事物、对象
洗衣服:面向对象的做法:1.用洗衣机洗;2.让洗衣店洗
面向过程思想:
面向过程思想就是把关注点放在一件事或一个活动中涉及到的步骤(也就是过程)上的思想(或思维方式)
面向过程关键字:步骤、过程
洗衣服: 面向过程的做法:打水 -> 放衣服 -> 放洗衣粉 -> 揉搓 -> 晾晒
面向对象和面向过程的区别
主要在于做事方式的不同,一个关注事物(对象),另一个过程(步骤)
类与对象
类:即归类,分类,是一系列具有相同属性和行为的事物的统称
对象:某一类事物的某个具体存在
类:属性和行为的集合,是一个抽象概念(手机)
对象:是该类事物的具体体现,是一种具体存在(魅族手机)
类的定义和使用
定义类的过程,就是把一系列相关事物共同的属性和行为抽取出来的过程
事物的属性,在类中叫成员变量
事物的行为,在类中叫成员方法
创建一个对象
类名 对象名 = new 类名();
使用一个对象
对象名.变量名
对象名.方法名(…)
例如:手机类的定义和使用
//Phone.java
public class Phone {
// 1.定义成员变量
String brand;
String model;
// 2.定义成员方法
public void call(String name) {
System.out.println("给" + name + "打电话");
}
public void playGame() {
System.out.println("用" + brand + model + "玩游戏");
}
}
//TestPhone.java
public class TestPhone {
public static void main(String[] args) {
//1.创建对象
Phone p = new Phone();
//2.调用成员变量,并打印
p.brand = "苹果";
p.model = "X";
System.out.println(p.brand);
System.out.println(p.model);
//3.调用成员方法
p.call("乔布斯");
p.playGame();
}
}
执行结果:
苹果
X
给乔布斯打电话
用苹果X玩游戏
成员变量和局部变量的区别
定义位置
成员变量:类中,方法外
局部变量:方法中,或形式参数
初始化值
成员变量:有默认初始化值
局部变量:无默认初始化值,必须先赋值再使用
作用范围
成员变量:在类中
局部变量:在方法中
内存中的位置
成员变量:堆内存
局部变量:栈内存
生命周期
成员变量:随着对象的创建而存在,随着对象的消失而消失
局部变量:随着方法的调用而存在,随着方法调用完毕而消失
注意:当方法中定义和类的属性名相同的变量,如何调用呢?
局部变量和成员变量重名时,采用就近原则
局部有就用,没有去本类成员位置找;有就用,没有就报错(后边学了继承还要去父类找)。
(比如上图中,有小黑,就是 小黑在努力学习了;没小黑就是张三丰,没张三丰就报错)
封装概述
把物体打包装到箱子里,然后封起来
把一系列功能打包到一台设备里,提供使用这些功能的界面(汽车、电脑、洗衣机…)
优点:
- 提高安全性:调用者不知道方法的具体实现
- 提高复用性 :方法可以被重复使用
- 将复杂的事情简单化:将繁多的代码以一个方法的方式呈现,通过调用方法就可以实现功能
private关键字
私有的,一种访问权限修饰符,用来修饰类的成员
被修饰的成员只能在本类中访问
//一般 private 用来修饰成员变量,public 用来修饰成员方法
private int age; //private 数据类型 变量名;
private void study() { } // private 返回值类型 方法名(参数列表) { } (一般不用)
//Student.java
private int age; // int age;
public void setAge(int a) {
/*可以做数据校验,但没必要,开发中前端已经校验过了
if (a > 200 || a < 0) { age = 1;} else { age = a; }*/
age = a;
}
public int getAge() { return age;}
//TestStudent.java
s.setAge(20); // s.age=18;
s.getAge();
this关键字
这,这个,表示本类对象的引用,本质是一个对象
每一个普通方法都有一个this,谁调用该方法,this就指向谁
this.属性名;
this.方法名(参数);
public class Student {
private int age;
public void setAge(int a) {
this.age = a;
}
public int getAge() {
return this.age;
}
标准代码:JavaBean
构造方法:
构建、创造,也叫构造器,用来帮助创建对象的方法,准确的说,构造方法的作用是初始化对象。
- 方法名必须与类名相同 ; 没有返回值 ; 没有返回值类型
修饰符 构造方法名(参数列表){
// 方法体
}
public class Student {
public Student(String name, int age) {
this.name = name;
this.age = age;
}
}
- 若未提供任何构造方法,系统会给出默认无参构造
- 若已提供任何构造方法,系统不再提供无参构造
- 构造方法可以重载
//TestStudent.java
public class TestStudent {
public static void main(String[] args) {
//一、通过无参构造实现
Student stu1= new Student();
stu1.setName("张三");
stu1.setAge(10);
System.out.println(stu1.getName()); //张三
System.out.println(stu1.getAge()); //10
//二、通过带参构造方法实现
Student stu2 = new Student("李四", 24);
System.out.println(stu2.getName()); //李四
System.out.println(stu2.getAge()); //24
}
}
//Student.java
public class Student {
//成员变量
private String name;
private int age;
//无参构造方法
public Student() { }
//带参构造方法
public Student(String name, int age) {
this.name = name;
this.age = age;
}
//公共访问方式:getXXX,setXXX
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; }
}
Java中封装的概念
将一系列相关事物共同的属性和行为提取出来,放到一个类中,隐藏对象的属性和实现细节,仅对外提供公共的访问方式。
隐藏对象数据的实现细节,意味着一个类可以全面的改变存储数据的方式,只要使用同样的方法操作数据,其它对象就不会知道或介意所发生的变化。
封装的关键:
绝对不能让类中的方法直接访问其它类的数据(属性),程序仅通过对象的方法与对象的数据进行交互。
Java编程思想_面向对象之继承
继承概述
泛指把前人的作风、文化、知识、财产等接受过来。
Java中的继承
通过扩展一个类来建立另外一个类的过程,叫做继承(inheritance)
通俗地说,所谓继承,就是让类与类之间产生父子关系。
所有的类都直接或间接的继承自: java.lang.Object 被继承的类叫做父类(基类、超类) 继承的类叫做子类(派生类)
格式(extends)
子类拥有了父类的非私有成员(成员变量、成员方法)
public class Parent { ..... }
public class Child extends Parent { }
public class Test {
public static void main(String[] args) {
Child c = new Child();
// c.age = 4; //错误 (私有成员变量)
c.setAge(2); //正确 (公共成员方法)
System.out.println(c.getAge());
}
}
继承的使用场景
多个类中存在相同的属性和行为时,可以将这些内容提取出来放到一个新类中,让这些类和新类产生父子关系,实现代码复用。
//Animal.java
public class Animal {
private String name;
private String sex;
private int age;
getXXX() ....
setXXX() ....
public void eat(){ System.out.println("会吃饭"); }
//Dog.java
public class Dog extends Animal {
public void watch(){ System.out.println("会看家"); }
}
//Cat.java
public class Cat extends Animal {
public void snore(){ System.out.println("会打鼾"); }
}
//TestAnimal.java
public class TestAnimal {
public static void main(String[] args) {
Dog d=new Dog();
d.eat();
d.watch();
Cat c=new Cat();
c.eat();
c.snore();
}
}
结果:
会吃饭
会看家
会吃饭
会打鼾
继承的优缺点
优点:功能复用,编译扩展新功能,结构清晰简化认识,易维护性
缺点:
打破了封装性(父类向子类暴露实现细节,打破父类封装性)
高耦合性(类与类依赖性高)
程序设计的追求:低耦合,高内聚
耦合:两个(或更多)模块相互依赖于对方
内聚:模块内部结构紧密,独立性强
继承关系中类成员的使用
继承关系中子父类成员变量的使用
- 查找变量的原则: 就近原则
- 查找变量的顺序: 局部变量成员变量父类更高的父类…Object
- 访问父类变量的方式: super.父类变量名;
- super: 当前对象父类的引用(父类内存空间的标识)
- 对象初始化顺序: 先初始化父类内容,再初始化子
this和super
this:本质:对象;用法:从本类开始找
super:本质:父类内存空间的标识 用法:从父类开始找
直接写变量名:局部变量
this.成员变量名:本类的成员变量
super.成员变量名:父类的成员变量
继承关系中子父类成员方法的使用
子父类中定义了同名的成员方法
. 定义重名方法的前提:
- 父类功不能完全满足现实需求,扩展父类功能
- 父类功能已过时,重新实现父类
- 查找方法的原则: 就近原则
- 查找方法的顺序: 本类父类更高的父类…Object
- 访问父类方法的方式: super.父类方法名();
- 定义重名方法的前提: 父类功不能完全满足现实需求,扩展父类功能 父类功能已过时,重新实现父类
继承关系中构造函数使用特点
创建子类对象时,优先调用父类构造
子类构造方法的第一行,隐含语句super(),用于调用父的类默认无参构造
子类创建对象时,必须先初始化该对象的父类内容,若父类中不存在默认无参构造,须手动调用父类其它构造
方法重写 (Override)
(上边九阴真经里的就是方法重写)
-
子类中出现和父类方法定义相同的方法的现象
-
方法重写也叫方法的复写、覆盖 方法名、参数列表、返回值类型都相同
-
父类私有方法无法重写
-
子类方法访问权限不能小于父类方法
-
子类不能比父类方法抛出更大的异常(了解) 扩展父类功能
-
场景:父类功能过时,重新实现父类功能
Java中的访问权限修饰符
private :自己用
默认(啥都不写):同包用
protected :子类用
public:大家用
方法重写和方法重载的区别
Java中继承的特点
单继承
Java只支持类的单继承,但是支持多层(重)继承
Java支持接口的多继承,语法为: 接口A extends 接口B,接口C,接口D…
私有成员不能继承
只能继承父类的非私有成员(成员变量、成员方法)
构造方法不能继承
构造方法用于初始化本类对象。
创建子类对象时,需要调用父类构造初始化该对象的父类内容,若父类构造可以被继承,该操作会造成调用的混乱。
继承体现了“is a”的关系
子类符合“is a(是一个)”父类的情况下,才使用继承,其它情况不建议使用
Java编程思想_面向对象之多态
多态概述
多种状态,同一对象在不同情况下表现出不同的状态或行为
水:液态水、水蒸气、固态冰…
米奇在不同的场景下有不同的角色:乐手、棒球手、糕点师
Java中实现多态的步骤
- 1.要有继承(或实现)关系
- 2.要有方法重写
- 3.父类引用指向子类对象(is a关系)
//Animal.java
public class Animal {
private String name;
public void eat(){
System.out.println("吃");
}
}
//Dog.java
public class Dog extends Animal { // 1
@Override
public void eat() {
System.out.println(getName() + "吃骨头"); // 2
}
}
//Test.java
public class Test {
public static void main(String[] args) {
Animal an = new Dog(); // 3
an.setName("哈士奇");
an.eat(); //哈士奇吃骨头
}
}
成员方法:编译看左(左边类有无这个成员),运行看右(右边类中该成员)
//Mouse.java
public class Mouse extends Animal{
@Override
public void eat(){
System.out.println(getName()+"吃奶酪");
}
}
//Test.java
public class Test {
public static void main(String[] args) {
Dog d = new Dog();
d.setName("哈士奇");
showAnimal(d); //哈士奇吃骨头
Mouse m = new Mouse();
m.setName("Jerry");
showAnimal(m); //Jerry吃奶酪
}
public static void showAnimal(Animal an) { //本来要写每个子类的showAnimal()
an.eat();
}
}
多态关系中成员变量的使用
编译看左,运行看左
public class Animal { String name = "animal"; }
public class Dog extends Animal { String name = "dog"; }
public class Test {
public static void main(String[] args) {
Animal an = new Dog();
System.out.println(an.name); //animal
Dog dog = new Dog();
System.out.println(dog.name); //dog
}
}
多态的好处和弊端
多态的好处:
可维护性:
基于继承关系,只需要维护父类代码,提高了代码的复用性,大大降低了维护程序的工作量
为什么要学习多态:
从程序的模块化和复用性来解释
封装:隐藏数据的实现细节,让数据的操作模块化,提高代码复用性
继承:复用方法,从对象的行为这个层面,提高代码的复用性
多态:复用对象,程序运行时同一个对象表现出不同的行为
可扩展性:
把不同的子类对象都当作父类看待,屏蔽了不同子类对象间的差异,做出通用的代码,以适应不同的需求,实现了向后兼容
多态的弊端:
父类引用不能使用子类特有成员(解决办法:向下转型(前提:必须准确知道该父类引用指向的子类类型))
类型转换:当需要使用子类特有功能时,需要进行类型转换
public class Animal {
public void eat() { System.out.println("吃饭"); }
}
public class Dog extends Animal {
@Override
public void eat() { System.out.println("狗吃骨头");}
//狗类独有的方法, 父类中是没有这个成员方法的.
public void watch() { System.out.println("狗会看家"); }
}
public class Test {
public static void main(String[] args) {
Animal an = new Dog();
an.eat(); //调用eat()方法
//优化后的方案: 判断当前对象是否是Dog类的对象, 如果是, 再调用watch()方法.
if(an instanceof Dog) {
Dog dog = (Dog)an;
dog.watch();
}
//an.watch(); //错误的,调用watch()方法, 属于子类独有的方法.
/* Dog dog = (Dog)an; //正确的写法
dog.watch(); */
//Cat c = (Cat)an; //不正常的转换(只能在继承层次内进行转换, 否则会报ClassCastException异常)
}
}
抽象类概述
抽象类:包含抽象方法的类。用abstract修饰。
抽象方法:只有方法声明,没有方法体的方法。用abstract修饰
抽象方法的由来:当需要定义一个方法,却不明确方法的具体实现时,可以将方法定义为abstract,具体实现延迟到子类
父类默认的eat方法已过时,所有子类都需要重写,所以将父类eat的方法体没有意义;但是每个动物都必须拥有eat功能,所以将父类eat定义成抽象方法。
//父类, 动物类(抽象类)
public abstract class Animal { /*...............*/
//抽象方法(特点: 要求子类必须重写)
public abstract void eat() ; /*...............*/
}
public class Dog extends Animal {
@Override
public void eat() { System.out.println("狗吃骨头"); }
}
public class Mouse extends Animal {
@Override
public void eat() { System.out.println("老鼠吃奶酪"); }
}
public class Test {
public static void main(String[] args) {
Animal an = new Dog();
an.eat();
}
}
抽象类的特点
抽象类的特点:
- 修饰符:必须用abstract关键字修饰
修饰符 abstract class 类名 {} // public abstract class Animal
修饰符 abstract 返回类型 方法名 {} // public abstract void eat(); - 抽象类不能被实例化,只能创建子类对象 // Animal an = new Animal(); 这样写是错误的.
- 抽象类子类的两个选择
重写父类所有抽象方法
定义成抽象类
抽象类成员的特点:
1.成员变量:
可以有普通的成员变量 也可以有成员常量(final)
2.成员方法:
可以有普通方法,也可以有抽象方法 抽象类不一定有抽象方法,有抽象方法的类一定是抽象类(或接口)
3.构造方法:
像普通类一样有构造方法,且可以重载
总结:
抽象类中的成员比普通类多一种: 抽象方法.
其他和普通类一样.
public abstract class Animal {
public Animal() {} // 3
public Animal(String name){ this.name = name;}
String name = "哈士奇"; //1
final int AGE = 30;
public abstract void eat(); // 2
public abstract void sleep();
public void call() { System.out.println("动物会叫"); }
}
public abstract class Dog extends Animal { }
public class Cat extends Animal {
@Override
public void eat() { System.out.println("猫吃鱼"); }
@Override
public void sleep() { System.out.println("猫躺着睡"); }
}
public class Test {
public static void main(String[] args) {
//Animal an = new Animal(); 这样写是错误的.(不能被实例化)
Animal an = new Cat();
an.name = "汤姆";
System.out.println(an.name);
//an.AGE = 50; 代码会报错, 原因是因为常量的值不能发生改变.
System.out.println(an.AGE);
}
}
一般步骤:
建父类:private成员变量,构造函数(不带参、带参),get,set方法,public自定义方法
建子类:构造函数(不带参、带参),重写父类方法,
建测试类:psvm,new对象,初始化(构造函数或set()),sout(get())
final关键字
最终的、最后的
用于修饰类、方法和变量
修饰类:该类不能被继承,但是可以继承其他类(不能有儿子,可以有爹) String, System
修饰方法:该方法不能被重写 不能与abstract共存
修饰变量:最终变量,即常量,只能赋值一次 不建议修饰引用类型数据,因为仍然可以通过引用修改对象的内部数据,意义不大
(一般也不用)
//final修饰的变量: 基本类型的变量, 值不能改变.
final int NUM = 20;
//NUM = 30; 代码报错, 常量值只能设置一次.
//final修饰的变量: 引用类型的变量, 是地址值不能改变,但是属性值可以发生变化.
final Employee em2 = new Employee();
//em2 = new Employee(); 代码报错, 原因是因为只要new就会开辟新空间.
em2.name = "张三";
em2.age = 23;
em2.name = "张无忌";
em2.age = 53;
static关键字
静态的
用于修饰类的成员 :
成员变量:类变量
成员方法:类方法
调用方式:
类名. 成员变量名;
类名. 成员方法名(参数);
static修饰成员变量:
被本类所有对象共享
注意事项:随意修改静态变量的值是有风险的,为了降低风险,可以同时用final关键字修饰,即公有静态常量(注意命名的变化)
public final static String DEPART_NAME = "研发部";
static修饰成员方法:
静态方法:静态方法中没有对象this,所以不能访问非静态成员
静态方法的使用场景:只需要访问静态成员 不需要访问对象状态,所需参数都由参数列表显示提供
(想通过类名. 的形式调用,而不是对象名. 的形式调用)
public class staticDemo {
int num = 10;
static int num2 = 20;
public static void show() {
System.out.println(num1); //错误(静态方法不能访问非静态成员)
System.out.println(num2); //正确
}
}
接口概述
接口技术用于描述类具有什么功能,但并不给出具体实现,类要遵从接口描述的统一规则进行定义,所以,接口是对外提供的一组规则、标准。
定义接口使用关键字interface
interface 接口名 {}
类和接口是实现关系,用implements表示
class 类名 implements 接口名
接口创建对象的特点
- 接口不能实例化
通过多态的方式实例化子类对象 - 接口的子类(实现类)
可以是抽象类(不用重写),也可以是普通类(必须重写父类所有抽象方法)
public interface Smoking {
public abstract void smoke(); //public abstract是默认的
}
public class Teacher implements Smoking {
@Override
public void smoke(){
System.out.println("抽烟有害健康");
}
}
public class Test {
public static void main(String[] args) {
// Smoking sm = new Smoking(); //错误,接口不能实例化
Smoking sm = new Teacher(); //正确,多态的方式实例化
sm.smoke();
}
}
接口和类之间的关系
类与类之间:继承关系,只能单继承,不能多继承,但是可以多层继承
类与接口之间:实现关系,可以单实现,也可以多实现
public class Mouse implements USB,A,B{ .... }
接口与接口之间的关系继承关系,可以单继承,也可以多继承
接口 extends 接口1,接口2,接口3…
public interface C extends A,B,USB{}
继承和实现的区别
继承体现的是“is a”的关系,对一个类来讲,父类中定义的都是:共性内容
实现体现的是“like a”的关系,对一个类来讲,父接口中定义的都是:扩展功能
接口成员的特点
接口成员变量的特点
接口没有成员变量,只有公有的、静态的常量:
public static final 常量名 = 常量值; //public static final默认的,不写系统也会自动加上
public interface USB {
public static final int NUM = 10;
}
public class Test {
public static void main(String[] args) {
USB.NUM = 20; //错误,常量值不能改
}
}
接口成员方法的特点
JDK7及以前,公有的、抽象方法:
public abstract 返回值类型 方法名();
JDK8以后,可以有默认方法和静态方法:
public default 返回值类型 方法名() {}
public static 返回值类型 方法名() {}
JDK9以后,可以有私有方法:
private 返回值类型 方法名() {}
//jdk7
public abstract void smoke();
//jdk8
public static void open(){
System.out.println("静态方法直接写");
}
public default void close(){
System.out.println("非静态方法必须加default");
}
//jdk9
private void method(){
System.out.println("jdk9");
}
接口构造方法的特点
接口不能够实例化,也没有需要初始化的成员,所以接口没有构造方法
public USB(){} //报错
图片去水印:
删去URL中的x-oss-process=image/watermark
直接将size改为1