N5 面向对象
作者:迷恋
一、面向过程&面向对象
面向过程思想
- 步骤清晰简单,第一步做什么,第二步做什么…
- 面向过程适合处理一些较为简单的问题
面向对象思想
- 物以类聚,分类的思维模式,思考问题首先会解决问题所需需要哪些分类,然后对这些分类进行单独思考。最后,才对某个分类下的细节进行面向过程的思索。
- 面向对象适合处理复杂问题,适合处理需要多人协作的问题!
对于描述复杂的事物,为了从宏观上把握、从整体上合理分析,我们需要使用面向对象的思路来分析整个系统。但是,具体到微观操作,仍需要面向过程的思路去处理。
-
面向对象编程的本质就是:以类的方式组织代码,以对象的组织(封装)数据。
-
三大特性:封装、继承、多态
-
从认识论角度考虑是先有对象后有类。对象,是具体的事物。类,是抽象的,是对对象的抽象。
从代码运行角度考虑是先有类后有对象。类是对象的模板。
二、类与对象的关系
类是一种抽象的数据类型,它是对某一类事物整体描述/定义,但是并不能代表某一个具体的事物
- 例如:动物、植物、手机、电脑…
- Person类、Pet类、Car类等,这些类都是用来描述/定义某一类具体事物应该具备的特点和行为。
对象是抽象概念的具体实例
- 张三就是人的一个具体实例,张三家里的旺财就是狗的一个具体实例。
- 能够体现出特点,展现出功能的是具体饿实例,而不是一个抽象的概念。
三、创建与初始化对象
- 使用new关键字创建对象
- 使用new关键字创建的时候,除了分配内存空间之外,还会给创建好的对象进行默认的初始化以及对类中构造器的调用。
- 类中的构造器也称为构造方法,是在进行创建对象的时候必须要调用的。并且构造器有以下两个特点:
- 1、必须和类的名字相同
- 2、必须没有返回类型,也不能写void
- 3、定义有参构造之后,如果想使用无参构造,就得显示地定义一个无参的构造。
实例:
//学生类
public class Student {
//属性:字段
//private私有:是保证封装性
private String name;
//构造函数,常用于属性的初始化
public Student(){
this.name = "小明";
}
//方法
public void study(){
System.out.println(this.name+"在学习");
}
//main方法
public static void main(String[] args) {
//类:抽象的,实例化
//类实例化后会返回一个自己的对象
//student对象就是一个student类的具体实例
Student xiaoming = new Student();
//调用xiaoming这个实例中的方法
xiaoming.study();
}
}
输出结果
小明在学习
四、封装
- 通常,应该禁止直接访问一个对象中的数据的实际表示,而应该通过操作接口来访问,这称为信息隐藏。
- 所谓类的封装是指在定义一个类时,将类中的属性私有化,即使用private关键字来修饰,私有化属性只能在它所在的类中被访问。为了能让外界访问私有属性,需要提供一些使用public修饰公有方法,其中包括用于获取属性值的getXxx()方法和设置属性值的setXxx()方法。
- 属性私有 ,get/set
实例:
1、首先创建一个Demo02.java如下:
package demo02;
public class Demo02 {
//private私有的
private String name;
private int age;
}
2、同包下创建Main.java如下:
package demo02;
public class Main {
public static void main(String[] args) {
Demo02 demo = new Demo02();
System.out.println(demo.name);
}
}
3、最终IDEA显示错误:
当一个类中的成员变量的修饰为private时,其它的类,像这里的Main类就无法直接获取类Demo02中的成员变量的值,当然也无法进行修改。
可以通过设置Demo02中一些方法来改变其值:
1、Demo03.java
package demo03;
public class Student {
private String name = "小明";
private int id;
private int age;
//get获得这个数据
public String getName() {
return this.name;
}
//set给这个数值赋值
public void setId(int id) {
this.id = id;
}
//可以在方法中添加一个条件判断,控制数据的合法性
public void setAge(int age) {
if (age>120||age<16){
}else
this.age = age;
}
//方便调用展示
public void show(){
System.out.println(this.id);
System.out.println(this.age);
}
}
2、同包下Main.java
package demo03;
public class Main {
public static void main(String[] args) {
Student student = new Student();
System.out.println(student.getName());
student.setId(2019220121);
student.setAge(130);
student.show();
}
}
输出结果
小明
2019220121
0
在实例中,setAge方法中可以通过设置一些条件来筛选外界传入的参数,以此来保证传入参数的合法性。
五、继承
继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模。
extends的意思是”拓展“。子类是父类的拓展。
Java中类只有单继承,没有多继承。
继承是类和类之间的一种关系。除此之外,类和类之间的关系还有依赖、组合、聚合等。
继承关系的两个类,一个为父类,一个为子类(派生类),一个为父类(基类)。子类继承父类,使用关键字extends来表示。
子类和父类之间,从意义上讲应该具有”is a“的关系。
子类会自动拥有父类所有可继承的属性和方法。
在Java中,所有的类,都默认直接或者间接继承Object
实例:
1、Person.java(父类)
package demo04;
public class Person{
String name;
int age;
public void say(){
System.out.println("说话");
}
}
2、同包下的XiaoMing.java(继承Person的子类)
package demo04;
public class XiaoMing extends Person{
}
3、同包下的Main.java
package demo04;
public class Main {
public static void main(String[] args) {
XiaoMing xiaoming = new XiaoMing();
xiaoming.name = "小明";
xiaoming.age = 16;
System.out.println(xiaoming.name+"的年龄是"+xiaoming.age);
xiaoming.say();
}
}
输出结果:
小明的年龄是16
说话
六、super
Java中专门提供了一个super关键字来用于访问父类的成员。例如访问父类的成员变量、成员方法和构造方法。
实例:
1、创建Person.java(父类)
package demo05;
public class Person {
protected String name = "人类";
}
2、同包下创建Student.java(子类)
package demo05;
public class Student extends Person {
private String name = "学生";
public void test(String name){
System.out.println(name);
System.out.println(this.name);
System.out.println(super.name);
}
}
3、同包下创建Main.java
package demo05;
public class Main {
public static void main(String[] args) {
Student student = new Student();
student.test("测试");
}
}
输出结果:
测试
学生
人类
实例2:
1、创建Person.java(父类)
package demo06;
public class Person {
public Person(){
System.out.println("父类的无参构造运行了");
}
}
2、同包下创建Student.java(子类)
package demo06;
public class Student extends Person{
public Student(){
System.out.println("子类的无参构造运行了");
}
}
3、同包下创建Main.java
package demo06;
public class Main {
public static void main(String[] args) {
Student student = new Student();
}
}
运行结果:
父类的无参构造运行了
子类的无参构造运行了
出现该结果的原因:
package demo06;
public class Student extends Person{
public Student(){
//因为继承自Person,此处默认隐藏含有一句
super();
System.out.println("子类的无参构造运行了");
}
}
而当语句这么写的时候:
package demo06;
public class Student extends Person{
public Student(){
System.out.println("子类的无参构造运行了");
super();
}
}
直接报错
提示:调用父类的构造器,必须要放在子类构造器的第一行
七、方法重写
在继承关系中,子类会继承自动继承父类中定义的方法,但有时在子类中需要对继承的方法进行一些修改,即对父类的方法进行重写。需要注意的是,在子类中重写的方法需要和父类中被重写的方法具有相同的方法名、参数列表以及返回值类型。
实例:
1、创建Father.java(父类)
package demo07;
public class Father {
public void test(){
System.out.println("Father");
}
}
2、同包下创建Son.java
package demo07;
public class Son extends Father{
public void test(){
System.out.println("Son");
}
}
3、同包下创建Main.java
package demo07;
public class Main {
public static void main(String[] args) {
Son son = new Son();
son.test();
}
}
运行结果:
Son
八、多态
- 即同一个方法可以根据发送对象的不同而采用多种不同的行为方式。
- 一个对象的实际类型是确定的,但可以指向对象的引用的类型有很多
- 多态存在的条件:
- 有继承关系
- 子类重写父类方法
- 父类引用指向子类对象
注意:多态是方法的多态,属性没有多态性。
实例:
1、创建Animal.java(父类)
package demo08;
public class Animal {
public void shout(){
System.out.println("父类");
}
}
2、同包下创建Cat.java(子类)
package demo08;
public class Cat extends Animal{
@Override
public void shout() {
System.out.println("Cat");
}
}
3、同包下创建Dog.java(子类)
package demo08;
public class Dog extends Animal{
@Override
public void shout() {
System.out.println("Dog");
}
}
4、同包下创建Main.java
package demo08;
public class Main {
public static void main(String[] args) {
Animal cat = new Cat();
Animal dog = new Dog();
cat.shout();
dog.shout();
}
}
输出结果:
Cat
Dog
对象的类型转换
在多态中,涉及到将子类对象当做父类类型使用的情况,例如:
Animal an1 = new Cat(); //将Cat对象当作Animal类型来使用
Animal an2 = new Dog(); //将Dog对象当作Animal类型来使用
将子类对象当作父类使用时,不需要任何显式的声明,需要注意的是,此时不能通过父类变量去调用子类中的某些方法。例:
1、创建Animal.java
package demo09;
public class Animal {
void shout(){
}
}
2、同包下创建Cat.java
package demo09;
public class Cat extends Animal{
public void shout(){
System.out.println("喵喵...");
}
public void sleep(){
System.out.println("猫睡觉...");
}
}
3、同包下创建Test.java
package demo09;
public class Test {
public static void main(String[] args) {
Animal cat = new Cat();
cat.shout();
cat.sleep();
}
}
此时报错如下:
可先将Animal类型的变量强制转换成Cat类型,如:
package demo09;
public class Test {
public static void main(String[] args) {
Animal cat = new Cat();
animalShow(cat);
}
public static void animalShow(Animal animal) {
Cat cat = (Cat) animal;
cat.shout();
cat.sleep();
}
}
运行结果:
喵喵...
猫睡觉...
- Java转型问题其实并不复杂,只需要记住一句话:父类引用指向子类对象。
例:有两个类,Father是父类,Son类继承自Father。
例1:
Father f1 = new Son(); //这就叫向上转型
//现在f1引用指向一个Son对象
Son s1 = (Son)f1; //这就叫向下转型
//现在f1还是指向Son对象
例2:
Father f2 = new Father();
Son s2 = (Son)f2; //出错,子类引用不能指向父类对象
你或许会问,第1个例子中:Son s1 = (Son)f1; 问为什么是正确的呢。
很简单因为 f1 指向一个子类对象,Father f1 = new Son(); 子类 s1 引用当然可以指向子类对象了。
而 f2 被传给了一个 Father 对象,Father f2 = new Father(); 子类 s2 引用不能指向父类对象。
- 总结:
1、父类引用指向子类对象,而子类引用不能指向父类对象。
2、把子类对象直接赋给父类引用叫upcasting向上转型,向上转型不用强制转换吗,如:
Father f1 = new Son();
3、把指向子类对象的父类引用赋给子类引用叫向下转型(downcasting),要强制转换,如:
f1 就是一个指向子类对象的父类引用。把f1赋给子类引用 s1 即
Son s1 = (Son)f1;
其中 f1 前面的(Son)必须加上,进行强制转换。
九、抽象类
- abstract修饰符可以用来修饰类,如果修饰方法,那么该该方法就是抽象方法;如果修饰类,那么该类就是抽象类。
- 抽象类中可以没有抽象方法,凡是有抽象方法的类一定要声明为抽象类。
- 抽象类,不能使用new关键字来创建对象,它是用来让子类继承的。
- 抽象方法,只有方法的声明,没有方法的实现,它是用来让子类实现的。
- 子类继承抽象类,那么就必须要实现抽象类没有实现的抽象方法,否则该子类也要声明为抽象类。
实例:
创建Actiom.java
package demo10;
//abstract抽象类
public abstract class Action {
//抽象方法,只有方法名字,没有方法的实现
public abstract void doSomething();
//1、不能new这个抽象类,只能靠子类去实现它:约束
//2、抽象类中可以写普通的方法
//3、抽象方法必须在抽象类中
//抽象的抽象:约束
}
2、同包下创建Action2
package demo10;
public class Action2 extends Action{
}
结果显示错误:
说明了:子类继承抽象类,那么就必须要实现抽象类没有实现的抽象方法,否则该子类也要声明为抽象类。
十、接口
- 普通类:只有具体实现
- 抽象类:具体实现和规范(抽象方法)都有!
- 接口:只有规范!自己无法写方法,专业的约束!约束和现实分离:面向接口编程。
接口中的方法默认是抽象方法。
(不可以实例化,接口中没有构造方法)- 接口就是规范,定义的是一组规则,体现了现实世界中“如果你是…则必须能…”的思想。如果你是天使,则必须能飞。如果你是汽车,则必须能跑…
- 接口的本质是契约。
- 声明类的关键词是class,声明接口的关键词是interface。
implements可以实现多个接口。
实现接口必须重写接口中的方法。
实例:
1、创建UserService.java
package demo11;
//interface关键字定义接口都需要有实现类
public interface UserService {
//接口中的所有定义都是抽象的abstract
//接口方法中不能有方法主体
public void run();
}
2、同包下创建UserServiceImp
(一般实现类的命名一般后缀使用Imp)
package demo11;
//抽象类:extends
//一般实现类使用关键字implements
//实现了接口的类,就需要重写接口中的方法
public class UserServiceImp implements UserService{
public void run() {
}
}
当实现多个接口时:
(同包下还含有一个TimeService.java)
package demo11;
//抽象类:extends
//一般实现类使用关键字implements
//实现了接口的类,就需要重写接口中的方法
/**
public class UserServiceImp implements UserService{
public void run() {
}
}
*/
public class UserServiceImp implements UserService,TimeService{
public void time() {
}
public void run() {
}
}
(TimeService.java):
package demo11;
public interface TimeService {
void time();
}
十一、内部类
内部类就是在一个类的内部再定义一个类,例如,A类中定义一个B类,那么B类相对A类来说就称为一个内部类,而A类相对于B类来说就是外部类了。
实例:
1、创建Outer.java
package demo12;
public class Outer {
private int id = 2019;
public void out(){
System.out.println("这是外部类的方法");
}
public class Inner{
public void in(){
System.out.println("这是内部类的方法");
}
//可以获得外部类的私有属性
public void getId(){
System.out.println(id);
}
}
}
2、同包下创建Application.java
package demo12;
public class Application {
public static void main(String[] args) {
Outer outer = new Outer();
//通过外部类来实现实例化内部类
Outer.Inner inner = outer.new Inner();
//调用内部类的方法
inner.in();
}
}