深入面向对象
- 面向对象三大特性
- static关键字详解
- 抽象类和接口
- 内部类及OOP实战
面向对象三大特性
封装
-
我们在程序设计中要追求“高内聚,低耦合”。
- 高内聚:就是类的内部数据操作细节由自己完成,不允许外部干涉
- 仅暴露少量的方法给外部使用
-
所以在设计时需要把一些数据与方法封装起来(数据的隐藏)
-
属性私有,get/set调用
-
作用
- 提高程序的安全性,保护数据
- 隐藏代码的实现细节
- 统一接口
- 增加了系统可维护性
-
代码演示
Student类
package com.oop.demo02; //学生类 public class Student { private String name; private int age; private String sex; 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 String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } }
Test类
package com.oop.demo02; //一个项目只能有一个main方法 public class Test { public static void main(String[] args) { //new 实例化一个对象 Student s1 = new Student(); s1.setName("成志恒"); s1.setAge(21); s1.setSex("男"); System.out.println(s1.getName()+" "+s1.getAge()+" "+s1.getSex()); } }
继承
-
继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模。
-
JAVA中类只有单继承,没有多继承
-
在java中,所有类都默认直接或者间接继承Object类
继承使用关键字extends来表示,代码示例如下
Person类
package com.oop.demo03; public class Person { public int money = 10000; public String name = "mayun"; }
Person类为父类,只定义了两个属性
Student类
package com.oop.demo03; public class Student extends Person{ public void print(){ System.out.println(this.money+" "+this.name); } }
Student类为子类,继承了父类的属性与方法
Test类
package com.oop.demo03; public class Test { public static void main(String[] args) { Student student = new Student(); student.print(); } }
结果:
由此可见,在Java中通过extends实现继承关系,而子类可以使用父类中的属性与方法(私有的不能调用)。
-
在继承中,当子类中有与父类相同的属性或方法时,可以使用super关键字来调用父类的属性或方法
-
super 可以理解为是指向自己超(父)类对象的一个指针,而这个超类指的是离自己最近的一个父类。
代码示例:
Person类
package com.oop.demo03; public class Person { public int money = 10000; public String name = "Person"; public void print(){ System.out.println("Person调用了"); } }
Student类
package com.oop.demo03; public class Student extends Person{ public int money = 50000; public String name = "Student"; public void print(){ System.out.println("Student调用了"); } public void test(String name){ System.out.println(name); System.out.println(this.name); System.out.println(super.name); } public void test1(){ print();//子类的方法 this.print();//子类的方法 super.print();//父类的方法 } }
Test
package com.oop.demo03; public class Test { public static void main(String[] args) { Student student = new Student(); student.test("成志恒"); System.out.println(); student.test1(); } }
运行结果:
可以看到,this调用的是Student类中本身的值,super调用了Person类中的值。方法亦如此
-
super调用构造器
Person类
package com.oop.demo03; public class Person { public Person(){ System.out.println("Person无参执行了"); } }
Student类
package com.oop.demo03; public class Student extends Person{ public Student(){ System.out.println("Student无参执行了"); } }
Test
package com.oop.demo03; public class Test { public static void main(String[] args) { Student student = new Student(); } }
由此可见在Student类中的无参构造器默认调用了Person类中的无参构造器,即隐藏使用了super()方法。
需要注意的是,super()方法必须要在子类构造器的第一行,否则系统会报错
-
super的注意点
- super调用父类的构造方法,必须在构造方法的第一个
- super必须只能出现在子类的方法或者构造方法中
- super与this不能同时调用构造方法
-
super与this的对比
-
代表的对象不同
this:本身调用者这个对象
super:代表父类对象的应用
-
前提
this:没有继承也可以使用
super:只能在继承条件才可以使用
-
构造方法
this():本类的构造
super():父类的构造
-
-
方法的重写(父类的功能,子类不一定需要,所以要重写)
- 需要有继承关系,子类重写父类的方法,与属性无关
- 重写只跟非静态方法有关,方法名必须相同
- 参数列表必须相同
- 子类的方法必须要和父类的一致,方法体不同
- IDEA快捷键:Alt+Insert :override;
代码:
父类B
package com.oop.demo03; public class B { public static void test1(){ System.out.println("B==>test"); } public void test2(){ System.out.println("B==>test"); } }
子类A
package com.oop.demo03; public class A extends B { public static void test1(){ System.out.println("A==>test"); } @Override//注解(重写用) public void test2() { System.out.println("A==>test"); } }
Test
package com.oop.demo03; public class Test { public static void main(String[] args) { A a = new A(); //父类的引用指向了子类 B b = new A(); a.test1(); b.test1(); System.out.println("=============="); a.test2();//子类重写了父类的方法 b.test2(); } }
-
静态方法和非静态方法的区别
- 静态方法:方法的调用只与左边定义的数据类型有关
- 非静态:重写
多态
-
即同一方法可以根据发送对象的不同而采用多种不同的行为方式
-
一个对象的实际类型是确定的,但可以指向对象的引用类型有很多
-
对象能执行哪些方法,主要看左边的类型,与右边关系不大
-
多态存在的条件
- 有继承关系
- 子类重写父类的方法
- 父类引用子类对象
代码示例
Person类
package com.oop.demo04; public class Person { public void run(){ System.out.println("run"); } }
Student类
package com.oop.demo04; public class Student extends Person{ }
Test
package com.oop.demo04; public class Test { public static void main(String[] args) { //一个对象的实际类型是可以确定的 //new Student //new Person //但可以指向的引用类型就不确定了:父类的引用指向子类 Student s1 = new Student(); Person s2 = new Student(); Object s3 = new Student(); s1.run(); s2.run(); } }
可以知道虽然s1,s2,s3的对象都是Student,但是它可以有不同的父类的引用,这就是方法的多态。
如果子类重写了父类的方法,则执行子类的方法
在Studunt类中重写run方法
package com.oop.demo04; public class Student extends Person{ @Override public void run() { System.out.println("son run"); } }
运行结果:
假如在子类中写一个独有的方法,s2可以调用吗?
Student类
package com.oop.demo04; public class Student extends Person{ @Override public void run() { System.out.println("son run"); } public void eat(){ System.out.println("eat"); } }
在Student类中加了一个eat方法
如果使用Test类中的s2调用这个方法,系统会报错
所以父类可以指向子类,但是不能调用子类独有的方法
-
多态的注意事项:
- 多态是方法的多态,属性没有多态性
- 父类和子类需要有联系,不然会有类型转换异常(ClassCastException)
-
instanceof 是 Java 的一个二元操作符,类似于 ==,>,< 等操作符。
-
instanceof 是 Java 的保留关键字。它的作用是测试它左边的对象是否是它右边的类的实例,返回 boolean 的数据类型。
package com.oop.demo05; public class Test { public static void main(String[] args) { //Object > String //Object > Person > Student //Object > Person > Teacher Object object = new Student(); System.out.println(object instanceof Student); System.out.println(object instanceof Person); System.out.println(object instanceof Object); System.out.println(object instanceof String); System.out.println(object instanceof Teacher); } }
需要注意的是若比较的两个类毫无联系,编译器会报错
-
类型转换
- 父类引用指向子类的对象
- 把子类转换为父类:向上转型
- 把父类转换为子类:向下转型,强制转换
package com.oop.demo05; public class Test { public static void main(String[] args) { Student student = new Student(); student.run();//子类能直接调用父类的方法 Person person = student; ((Student)person).eat();//父类调用子类的方法需要强制转换 } }
Static关键字详解
-
static标注的属性或方法为静态属性或方法。
-
static(静态)方法只会在类加载的时候执行一次。
package com.oop.demo06; public class Person { //先与构造方法执行,可以用来定义常量 { System.out.println("匿名代码块"); } //最先执行,但只执行一次 static { System.out.println("静态代码块"); } //最后执行 Person(){ System.out.println("构造方法"); } public static void main(String[] args) { Person person = new Person(); System.out.println("================"); Person person1 = new Person(); } }
-
static关键字还可以用来静态导入包
正常来说要使用Math类的方法每一次都需要在方法前面加"Math."
System.out.println(Math.random());
否则会报错
但是如果通过static导入包的话,就可以直接使用了
package com.oop; import static java.lang.Math.random; public class Tset { public static void main(String[] args) { System.out.println(random()); } }
抽象类和接口
抽象类
-
abstract修饰符可以用来修饰方法也可以修饰类,如果修饰方法,那么该方法就是抽象方法,如果修饰类,那么该类就是抽象类
package com.oop.demo08; //抽象类 public abstract class Action { //抽象方法 public abstract void run(); }
-
抽象类中可以没有抽象方法,但是有抽象方法的类一定要声明为抽象类
package com.oop.demo08; public abstract class Action { public abstract void run(); public void go(){ System.out.println("go"); } }
-
抽象类不能new 只能靠子类去实现它
-
抽象类存在的意义:在一些复杂的项目中,把公有属性抽象出来,再使用时只需要继承这个抽象类即可,提高开发效率。
接口
-
接口就是规范,定义的是一组规则,体现了现实世界中“如果你是…则必须能…”的思想。
-
接口的本质是契约。
-
接口中的所有定义的方法都是public abstract的;属性为常量。
-
接口定义的关键字为interface,接口都需要有实现类
接口UserService
package com.oop.demo09; public interface UserService { void add(String name); void delete(String name); void update(String name); void query(String name); }
实现类UserServiceImp
package com.oop.demo09; //实现了接口的类就必须要实现接口中的所有方法 public class UserServiceImp implements UserService{ @Override public void add(String name) { } @Override public void delete(String name) { } @Override public void update(String name) { } @Override public void query(String name) { } }
-
利用接口可以实现多继承!
接口TimeService
package com.oop.demo09; import java.sql.Time; public interface TimeService { Time timer(); }
接口RunService
package com.oop.demo09; public interface RunService { void run(); }
实现类TestImp
package com.oop.demo09; import java.sql.Time; public class TestImp implements TimeService,RunService{ @Override public void run() { } @Override public Time timer() { return null; } }
内部类
- 内部类就是在一个类的内部再定义一个类,详解见内部类解析
- 成员内部类
- 静态内部类
- 局部内部类
- 匿名内部类