面向对象
- 面向对象编程(Object-Oriented Programming,OOP)
- 面向对象编程的本质是:以类的方式组织代码,以对象的组织(封装)数据。
什么是面向对象
面向对象的方法主要是把事物给对象化,包括其属性和行为。面向对象编程更贴近实际生活的思想。总体来说面向对象的底层还是面向过程,面向过程抽象成类,然后封装,方便使用就是面向对象(万物皆对象)。
面向对象和面向过程的区别
面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了;面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。
创建和初始化对象
- 使用new关键字创建对象时,除了分配内存空间外,还会给创建好的对象进行默认的初始化,以及对类中构造器的调用
- Student.java
package com.kuangshen.OOP;
public class Student {
String name;
int age;
public void study()
{
System.out.println(this.name + "在学习" + " 他已经" + this.age + "岁了");
}
}
- Application.java:
package com.kuangshen.OOP;
public class Application {
public static void main(String args[])
{
Student student1 = new Student();
Student student2 = new Student();
student1.name = "小明";
student1.age = 12;
student2.name = "小红";
student2.age = 11;
System.out.println(student1.name + " " + student1.age);
}
}
-
类中的构造器也称为构造方法,是在进行创建对象的时候必须调用的。并且构造器有以下两个特点:
- 必须和类的名字相同
- 必须没有返回类型,也不能写void
-
其作用如下:
- 利用new调用构造器
- 初始化对象的值
-
案例如下:
-
Person.java
-
package com.kuangshen.OOP; public class Person { //一个类即使什么都不写也会有一个默认构造器方法。与类名相同并且没有返回值 String name; int age; public Person() { this.name= "神州";//可以什么都不写 } public Person(String name) { this.name = name; } public Person(int age, String name) { this.age = age; this.name = name; } }
-
Application.java
-
package com.kuangshen.OOP; public class Application { public static void main(String args[]) { Person person = new Person(); System.out.println(person.name); } }
-
-
注意:定义有参构造之后,如果想要使用无参构造,我们需要显示的定义一个无参构造(在一般条件下,无参构造不用写,系统也会默认自带一个)。
-
下面例子会报错,需要把Person.java中的无参构造的注释去掉:
-
package com.kuangshen.OOP; public class Person { //一个类即使什么都不写也会有一个默认构造器方法。与类名相同并且没有返回值 String name; int age; /*public Person() { this.name= "神州";//可以什么都不写 }*/ public Person(String name) { this.name = name; } }
package com.kuangshen.OOP; public class Application { public static void main(String args[]) { Person person = new Person(); System.out.println(person.name); } }
-
封装
-
该露的露,该藏的藏
- 我们程序设计要追去高内聚,低耦合。高内聚就是类的内部数据操作细节自己完成,不允许外部干涉;低耦合就是仅暴露少量的方法给外部使用。
-
封装:数据的隐藏
- 通常,应禁止直接访问一个对象中数据的直接表示,而应通过操作接口来访问,这称为信息隐藏。
-
封装中提出了private关键字,利用这个关键字,可以避免对数据的直接表示,代码如下:
-
ackage com.kuangshen.OOP; public class Teacher { private int age; private String name; public void setName(String name) { this.name = name; } public String getName() { return this.name; } }
-
package com.kuangshen.OOP; public class Application { public static void main(String args[]) { Teacher teacher = new Teacher(); teacher.setName("天才"); System.out.println(teacher.getName()); // System.out.println(teacher.name);//不可以对数据直接操作 } }
-
继承
-
继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
-
继承一般使用extend关键字,其中people.java,people1.java和Application.java的例子如下:
-
package com.kuangshen.OOP; public class people { private int age; public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
-
package com.kuangshen.OOP; public class people1 extends people{ public void say() { System.out.println(getAge()); } }
-
package com.kuangshen.OOP; public class Application { public static void main(String args[]) { people1 person = new people1(); person.setAge(5); person.say(); } }
-
-
继承类型
- 需要注意的是 Java 不支持多继承,但支持多重继承。
-
继承的特性
- 子类拥有父类非 private 的属性、方法。
- 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
- 子类可以用自己的方式实现父类的方法。
- Java 的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如 B 类继承 A 类,C 类继承 B 类,所以按照关系就是 B 类是 C 类的父类,A 类是 B 类的父类,这是 Java 继承区别于 C++ 继承的一个特性。
- 提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系越紧密,代码独立性越差)。
-
继承关键字
- 继承可以使用 extends 和 implements 这两个关键字来实现继承,而且所有的类都是继承于 java.lang.Object,当一个类没有继承的两个关键字,则默认继承object(这个类在 java.lang 包中,所以不需要 import)祖先类。
-
super关键字
-
super关键字:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。
this关键字:指向自己的引用。
后面people.java,people1.java和Application.java的例子如下:
package com.kuangshen.OOP; public class people { protected String name = "天下"; public void print() { System.out.println("1"); } }
package com.kuangshen.OOP; public class people1 extends people{ private String name = "月关"; public void print() { System.out.println("2"); } 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(); } }
package com.kuangshen.OOP; public class Application { public static void main(String args[]) { people1 person = new people1(); person.test("天马"); person.test1(); } }
最终的结果如下:
天马 月关 天下 2 2 1
-
-
super构造器
-
super无参构造器
package com.kuangshen.OOP; public class people1 extends people{ public people1() { //隐藏代码,调用了父类的构造器 super();//可加可不加,但是加了必须在第一行。 System.out.println("使用了子类构造器"); } }
package com.kuangshen.OOP; public class people { public people() { System.out.println("使用了父类构造器"); } }
package com.kuangshen.OOP; public class Application { public static void main(String args[]) { people1 person = new people1(); } }
-
super有参构造器
package com.kuangshen.OOP; public class people1 extends people{ public people1() { //影藏代码,调用了父类的构造器 super("人间");//当有参时,如果super不写,那么子类也要变成有参构造,如果写,super要变成有参构造 System.out.println("使用了子类构造器"); } }
package com.kuangshen.OOP; public class people { public people(String a) { System.out.println("使用了父类" + a +"构造器"); } }
package com.kuangshen.OOP; public class Application { public static void main(String args[]) { people1 person = new people1(); } }
-
-
super注意点
- super调用父类的构造方法,必须在构造方法的第一个
- super只能出现在子类的方法或者构造方法中
- super和this不能同时调用构造方法
-
super Vs this
-
代表的对象不同
- this:调用本身的对象
- super:代表父类对象的应用
-
前提:
- this:没有继承也可以使用
- super:只能在继承条件才可以使用
-
构造方法:
- this();本类的构造
- super();父类的构造
-
重写
- 加上static,以下分别为A.java,B.java和Application.java
package com.kuangshen.OOP;
public class A extends B{
public static void test()
{
System.out.println("A");
}
}
package com.kuangshen.OOP;
public class B {
public static void test()
{
System.out.println("B");
}
}
package com.kuangshen.OOP;
public class Application {
public static void main(String args[])
{
//方法的调用只和左边,定义的数据类型有关
A a = new A();
a.test();
B b = new A();//父类的引用指向了子类
b.test();
}
}
输出结果为
A
B
-
重写的例子(重写只和非静态有关)
package com.kuangshen.OOP; public class A extends B{ //Override重写 @Override//注解:有功能的注释 public void test() { System.out.println("test=>A"); } }
package com.kuangshen.OOP; public class B { public void test() { System.out.println("test=>B"); } }
package com.kuangshen.OOP; public class Application { public static void main(String args[]) { //方法的调用只和左边,定义的数据类型有关 A a = new A(); a.test(); B b = new A();//非静态时,子类重写了父类的方法,结果只和子类的结果有关 b.test(); } }
最终的结果为:
A
A
-
重写的注意点:需要有继承关系,子类继承父类的方法
- 方法名必须相同
- 参数列表必须相同(否则就是重载)
- 修饰符:范围可以扩大但不可以缩小:public>Protected>Default>private
- 抛出的异常:范围可以缩小但不可以扩大
-
重写,子类和父类必须要一致,但方法体不同
-
为什么需要重写:父类的方法,子类不一定需要,或者不一定满足
多态
- 多态,即同一方法根据发送对象的不同而采用不同的行为方式。
- 一个对象的实际类型是确定的,但可以指向对象的引用类型有很多(父类、有关系的类)
- Student.java,Person.java和Application.java的案例如下:
package com.kuangshen.OOP;
public class Student extends Person{
@Override
public void run() {
System.out.println("Student");
}
public void eat()
{
System.out.println("eat");
}
}
package com.kuangshen.OOP;
public class Person {
public void run()
{
System.out.println("Person");
}
}
package com.kuangshen.OOP;
public class Application {
public static void main(String args[])
{
Student s1 = new Student();
Person s2 = new Student();
Object s3 = new Student();
//对象能执行哪些方法,只能看对象左边的类型,和右边关系不大
s1.run();
s2.run();
s1.eat();
((Student) s2).eat();//父类无法调用子类的方法,只能强制转换成子类
//s2.eat;//会报错
}
}
输出结果:
Student
Student
eat
eat
-
多态注意事项:
- 多态是方法的多态,属性没有多态
- 多态两个类之间有继承关系,如父类和子类
- 存在条件:两类之间继承关系;方法需要重写;父类的引用指向子类,如:Father f1 = new Son();
-
以下方法无法重写,自然没有多态
- static方法,属于类,它不属于实例
- final常量
- private方法
instanceof
直接看例子:
package com.kuangshen.OOP;
public class Application {
public static void main(String args[])
{
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 Teacher);
System.out.println(object instanceof String);
System.out.println("=======================");
Person person = new Student();
System.out.println(person instanceof Student);
System.out.println(person instanceof Person);
System.out.println(person instanceof Object);
System.out.println(person instanceof Teacher);
//System.out.println(person instanceof String);//编译失败
System.out.println("=======================");
Student student = new Student();
System.out.println(student instanceof Student);
System.out.println(student instanceof Person);
System.out.println(student instanceof Object);
//System.out.println(student instanceof Teacher);//编译失败
//System.out.println(student instanceof String);//编译失败
//Object > String
//Object > Student > Person
//Object > Teacher > Person
}
}
我们可以发现,对于A a = new B()[其中A是B的父类或A = B]中,如果A和B没有父子关系,那么编译失败。在后面的(a instanceof C)中如果B是C的子类或者B=C,那么返回值为true,否则返回值为false
类型强制转换
先看例子:
package com.kuangshen.OOP;
public class Application {
public static void main(String args[])
{
Person person = new Student();
((Student) person).eat();
}
}
当父类转化为子类时,要强制转换,本质原因是子类可以调用父类的方法,但父类不可以调用子类的方法。
注意点:
- 父类引用指向子类的对象
- 把子类转换为父类,向上转型,不用强制转换
- 把父类转换为子类,向下转型,需要强制转换
- 强制转换的目的是为了方便方法的调用,减少重复的代码
static关键字
- 静态static和类名一起加载在内存中,所以使用时有一些好用的特性。
package com.kuangshen.OOP;
public class Bus {
private static int age;
private double score;
public void run()
{}
public static void go()
{}
public static void main(String args)
{
Bus bus = new Bus();
System.out.println(Bus.age);//对于静态变量,可以用类名直接调用。一般工作时尽量这样调用,方便知道这个age是静态变量。
//System.out.println(Bus.score);//报错
System.out.println(bus.age);
System.out.println(bus.score);
bus.run();
bus.go();//一般都是这样调用
//Bus.run();//这样会报错
Bus.go();//static的特性可以用类名直接调用
go();//由于main函数在类名中,所以可以前面不加类名直接调用,所以这种格式不能在别的.java文件中这样写
}
}
- 静态代码块
package com.kuangshen.OOP;
public class Bus {
//顺序2:一般用来赋初值
{
System.out.println("匿名代码块");
}
//顺序1:只执行依次
static {
System.out.println("静态代码块");
}
public Bus(){
System.out.println("构造方法");
}
public static void main(String args[])
{
Bus bus1 = new Bus();
System.out.println("=========");
Bus bus2 = new Bus();
}
}
最终输出结果如下:
静态代码块
匿名代码块
构造方法
@@@@@@@@@@@@@@@@@@@@
匿名代码块
构造方法
- 静态导入包
final特性
如果一个类用了final关键字,就不能被继承。
package com.kuangshen.OOP;
public final class Person {
public void run()
{
System.out.println("Person");
}
}
package com.kuangshen.OOP;
public class Student extends Person{//这里会报错,无法继承有final关键字的(常量无法被继承)
@Override
public void run() {
System.out.println("Student");
}
public void eat()
{
System.out.println("eat");
}
}
抽象类
例子:
package com.kuangshen.OOP;
public abstract class Action {
public abstract void dosomething();
}
package com.kuangshen.OOP;
public class ActionMain extends Action {
//当一个子类继承了抽象类时会报错,必须要重写抽象方法。
//如果子类也是抽象类,那么要在子子类去重写抽象方法
@Override
public void dosomething() {
}
}
- 不能new这个抽象类,只能靠子类去实现它。
- 抽象类中可以写普通的方法。
- 抽象方法必须写在抽象类中。
抽象类的用处:比如你开发一个游戏,不同的游戏任务某项数据不一样,那么利用抽象类方法名不变,只需要在其继承中重写方法就可以了,本质是为了提高开发效率。
抽象类需要继承,由于java中都是单继承,所以泛用性大大降低;因为接口可以多继承,所以java中最常用到的是接口。
接口
- 接口可以理解为一种特殊的类,里面全部是由****全局常量*和公共的抽象方法所组成。接口是解决***Java无法使用多继承*的一种手段,但是接口在实际中更多的作用是*制定标准*的。或者我们可以直接把接口理解为*100%的抽象类****,既接口中的方法****必须全部****是抽象方法。
- 接口注意点:
- 接口是为了定义一些方法,让不同的人去实现
- 接口中默认的方法都是public abstract
- 接口中的常量都是public static final
- 接口和抽象类一样不能被实例化,因为接口中没有构造方法
- 通过implements关键字可以实现多个接口
- 实现接口时必须要重写接口里面的方法
下面是userService.java,timeService.java,userServiceImpl.java的例子
package com.kuangshen.OOP.demo01;
public interface UserService {//接口通过interface定义
public abstract void a();//在IDEA中,接口中的方法都默认公共并且抽象的,所以public和abstract可以不写
void add();
void delete();
void update();
void query();
}
package com.kuangshen.OOP.demo01;
public interface TimeService {
void timer();
}
package com.kuangshen.OOP.demo01;
public class UserServiceImpl implements UserService, TimeService {//接口可以多继承
@Override
public void a() { }
@Override
public void add() { }
@Override
public void delete() { }
@Override
public void query() { }
@Override
public void update() { }
@Override
public void timer() { }
}
内部类
package com.kuangshen.OOP.neibulei;
public class Outer {
private int id = 10;
public void out()
{
System.out.println("这是外部类的方法");
}
public class Inner
{
public void in()
{
System.out.println("这是内部类的方法");
}
//内部类可以获得外部类的私有属性
public void getID()
{
System.out.println(id);
}
}
}
package com.kuangshen.OOP.neibulei;public class MainClass { public static void main(String args[]) { Outer outer = new Outer(); outer.out(); Outer.Inner inner = outer.new Inner();//内部类的定义方法 inner.in(); inner.getID(); }}
输出结果:
这是外部类的方法
这是内部类的方法
10