Java面向对象
面向过程&面向对象
面向过程:
- 步骤清晰简单,第一步做什么,第二步做什么......
- 面向过程适合处理一些较为简单的事情
面向对象:
- 物以类聚,分类的思维模式。思考问题首先会解决问题需要哪些分类,然后对这些分类进行单独思考。最后,才对某个分类下的细节进行面向过程的思索。
- 面向对象适合处理复杂的问题,适合处理需要多人协作的问题。
概述:对于描述复杂的事物,为了从宏观上把握、从整体上合理分析,我们需要使用面向对象的思路来分析整个系统。但是,具体到微观操作,仍然需要面向过程的思路去处理。
什么是面向对象?
面向对象(Object-Oriented Programming, OOP)的本质是以类的方式组织代码,以对象的方式封装数据。
三大特性:
- 封装
- 继承
- 多态
注意:
- 从认识论的角度考虑,是先有对象后有类。因为对象是具体的,而类是抽象的。类是对对象的抽象。
- 从代码运行的角度考虑,是先有类后有对象。类是对象的模板。
类与对象的关系
类是一种抽象的数据类型。它是对某一类事物的整体描述或定义,但并不能代表某一个具体的事物。
- 如:人、动物、植物、电脑、手机,等等......
对象是抽象概念的具体实例。
- 如:张三、隔壁家的小花猫咪咪、《唐伯虎点秋香》里的那只名叫旺财的狗
初始化与创建对象
创建的方式:使用new关键字创建对象
使用new关键字创建对象的时候,除了分配内存空间之外,还会给创建好的对象赋默认值进行初始化,以及调用类中的构造器。
示例:
Student类
package com.wmwx.oop.Demo01;
//学生类
public class Student {
//属性:字段
String name;
int age;
//方法
public void study(){
System.out.println("学生"+this.name+"在学习。");
}
}
Application类(启动类)
package com.wmwx.oop.Demo01;
//启动类
public class Application {
//一个项目应该只存在一个main方法
public static void main(String[] args) {
//类是抽象的,需要实例化
//类实例化后会返回一个自己的对象
//student对象是Student类的一个实例
Student student = new Student();
student.study(); //输出"学生null在学习。"
student.name = "小明";
student.study(); //输出"学生小明在学习。"
}
}
构造方法
当一个对象被创建时候,构造方法用来初始化该对象。构造方法和它所在类的名字相同,但构造方法没有返回值。
一个类即使什么都不写,也会存在一个构造方法。因为 Java 自动提供了一个默认构造方法,其访问修饰符和类的访问修饰符相同。
一旦自己定义了构造方法,默认的构造方法就会失效。
示例:
Person类:
package com.wmwx.oop.Demo01;
public class Person {
String name;
//使用快捷键alt+insert可以自动生成构造方法
//无参构造
public Person(){
this.name = "一个无名氏";
}
//有参构造(一旦定义有参构造,就必须显式定义无参构造)
public Person(String name){
this.name = name;
}
}
Application类:
package com.wmwx.oop.Demo01;
public class Application {
public static void main(String[] args) {
//使用new关键字,本质是在调用构造方法
Person person1 = new Person(); //调用无参构造
System.out.println(person1.name); //输出"一个无名氏"
//利用构造方法,可以初始化对象
Person person2 = new Person("惟妙惟霄"); //调用有参构造
System.out.println(person2.name); //输出"惟妙惟霄"
}
}
内存分析
过程如下:
- 创建类
- 在堆中存放类和类中的静态方法
- 创建对象
- 在堆中为对象开辟空间
- 在栈中存放对象的引用变量名
- 令对象的引用变量名指向堆中开辟的空间
封装
所谓封装,即该露的露,该藏的藏。程序设计要追求“高内聚,低耦合”。
高内聚:类的内部数据操作细节自己完成,不允许外部干涉。
低耦合:仅暴露少量的方法给外部使用。
对于代码而言,总结起来就一句话:属性私有,get/set。
意义:
- 提升程序的安全性,保护数据
- 隐藏代码的实现细节
- 统一接口
- 提高了系统的可维护性
示例:
Student类:
package com.wmwx.oop.Demo03;
public class Student {
//属性私有
private String name;
private int id;
private String gender;
private int age;
//需要提供共有的get和set方法
//get方法:获取数据
public String getName() {
return name;
}
//set方法:设置数据
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if (age<0||age>120){
this.age = 3;
}else{
this.age = age;
}
}
}
Application类:
package com.wmwx.oop;
import com.wmwx.oop.Demo03.Student;
//启动类
public class Application {
public static void main(String[] args) {
Student student = new Student();
student.setName("惟妙惟霄");
student.setId(123456);
student.setGender("男");
student.setAge(130);
System.out.println(student.getAge()); //输出3
}
}
继承
继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模。
在 Java 中通过 extends 关键字可以声明一个类是从另外一个类继承而来的,其语法如下:
class 子类 extends 父类 {
}
extends的意思是扩展,子类是对父类的扩展。
继承的注意事项:
-
Java只有单继承,没有多继承。
-
在Java中,所有类都默认直接或间接继承自Object类。
在Java中,可以使用this指代当前类,并使用super指代父类。
super的注意事项:
- super调用父类的构造方法,必须在构造方法的第一行。
- super只能出现在子类的方法或构造方法中。
- super和this不能同时调用构造方法。
重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。重写的好处在于子类可以根据需要,定义特定于自己的行为。 也就是说,子类能够根据需要实现父类的方法。重写需要遵守以下规则:
- 方法名必须相同
- 参数列表必须相同
- 修饰符的范围可以扩大,但不能缩小
- 抛出的异常可以被缩小,但不能扩大
示例:
Person类:
package com.wmwx.oop.Demo04;
//父类:人类
public class Person {
private int money = 10_0000_0000;
protected String name = "惟妙惟霄";
public Person() {
System.out.println("Person的无参构造方法执行了!");
}
public void say(){
System.out.println("说了一句话。");
}
public int getMoney() {
return money;
}
public void setMoney(int money) {
this.money = money;
}
public void print(){
System.out.println("Person");
}
}
Student类:
package com.wmwx.oop.Demo04;
//子类:学生类
public class Student extends Person{
private String name = "妙霄";
public Student() {
//隐藏代码:super();
//父类的构造方法必须要在子类的构造方法的第一行
System.out.println("Student的无参构造方法执行了!");
}
public void test1(String name){
System.out.println(name); //输出参数
System.out.println(this.name); //输出当前类的name
System.out.println(super.name); //输出父类的name
}
//重写都是方法的重写,与属性无关
//只允许重写public方法
//可以使用快捷键alt+insert来插入重写方法
@Override
public void print() {
System.out.println("Student");
}
public void test2(){
print();
this.print();
super.print();
}
}
Application类:
package com.wmwx.oop;
import com.wmwx.oop.Demo04.Person;
import com.wmwx.oop.Demo04.Student;
//启动类
public class Application {
public static void main(String[] args) {
Student student = new Student();
//第一行输出"Person的无参构造方法执行了!"
//第二行输出"Student的无参构造方法执行了!"
student.say(); //子类继承父类,就会拥有父类的public方法
System.out.println(student.getMoney()); //可以用父类的get/set方法对属性进行操作
//可以使用快捷键ctrl+H来查看继承树
student.test1("MiaoXiao");
//第一行输出"MiaoXiao"
//第二行输出"妙霄"
//第三行输出"惟妙惟霄"
student.test2();
//第一行输出"Student"
//第二行输出"Student"
//第三行输出"Person"
Student stu1 = new Student();
stu1.print(); //输出"Student"
//父类的引用指向了子类
Person stu2 = new Student();
stu2.print(); //输出"Student"
}
}
多态
多态是同一个行为具有多个不同表现形式或形态的能力。当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。
多态存在的三个必要条件:
- 继承
- 重写
-
父类引用指向子类对象:
Parent p = new Child();
不能被重写的方法:
- static 方法
- final 方法
- private 方法
示例:
Person类:
package com.wmwx.oop.Demo05;
public class Person {
public void run(){
System.out.println("人在跑步。");
}
}
Student类:
package com.wmwx.oop.Demo05;
public class Student extends Person{
@Override
public void run() {
System.out.println("学生在跑步。");
}
public void eat(){
System.out.println("学生在吃东西。");
}
}
Application类:
package com.wmwx.oop;
import com.wmwx.oop.Demo05.Person;
import com.wmwx.oop.Demo05.Student;
//启动类
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(); //输出"学生在吃东西"
//s2.eat(); //不能调用。对象能使用哪些方法,要看左边的类型。
((Student)s2).eat(); //强制类型转换。输出"学生在吃东西。"
}
}
instanceof
Java中的instanceof关键字可以用来判断某一个对象是不是某一个类是实例。如果是,返回true;如果不是,返回false;如果二者无关,则编译不通过。
示例:
package com.wmwx.oop;
import com.wmwx.oop.Demo06.Person;
import com.wmwx.oop.Demo06.Student;
import com.wmwx.oop.Demo06.Teacher;
//启动类
public class Application {
public static void main(String[] args) {
//继承关系如下:
//Object -> Person -> Student
//Object -> Person -> Teacher
//Object -> String
Object object = new Student();
System.out.println(object instanceof Student); //输出true
System.out.println(object instanceof Person); //输出true
System.out.println(object instanceof Object); //输出true
System.out.println(object instanceof Teacher); //输出false
System.out.println(object instanceof String); //输出false
System.out.println("=====");
Person person = new Student();
System.out.println(person instanceof Student); //输出true
System.out.println(person instanceof Person); //输出true
System.out.println(person instanceof Object); //输出true
System.out.println(person instanceof Teacher); //输出false
//System.out.println(person instanceof String); //编译时报错
System.out.println("=====");
Student student = new Student();
System.out.println(student instanceof Student); //输出true
System.out.println(student instanceof Person); //输出true
System.out.println(student instanceof Object); //输出true
//System.out.println(student instanceof Teacher);//编译时报错
//System.out.println(student instanceof String); //编译时报错
}
}
类型转换
- 子类转换为父类,是向上转型,可自动转换。
- 父类转换为子类,是向下转型,需强制转换。
示例:
Person类:
package com.wmwx.oop.Demo06;
public class Person {
public void run(){
System.out.println("人在跑步。");
}
}
Student类:
package com.wmwx.oop.Demo06;
public class Student extends Person{
public void walk(){
System.out.println("学生在走路。");
}
}
Application类:
package com.wmwx.oop;
import com.wmwx.oop.Demo06.Person;
import com.wmwx.oop.Demo06.Student;
//启动类
public class Application {
public static void main(String[] args) {
//高 ----------------- 低
Person person = new Student();
((Student)person).walk(); //强制类型转换
Student student = new Student();
Person obj = student; //子类转换为父类,可能会丢失一些方法
//obj.walk(); //编译时报错
}
}
静态代码块
静态代码块会在类加载时执行,且只会执行一次。
示例:
Person类:
package com.wmwx.oop.Demo07;
public class Person {
//第二个执行,可在这里赋初始值
{
System.out.println("匿名代码块");
}
//第一个执行,只执行一次
static {
System.out.println("静态代码块");
}
//第三个执行
public Person() {
System.out.println("构造方法");
}
}
Application类:
package com.wmwx.oop;
import com.wmwx.oop.Demo07.Person;
//启动类
public class Application {
public static void main(String[] args) {
Person person = new Person();
//第一行输出"静态代码块"
//第二行输出"匿名代码块"
//第三行输出"构造方法"
}
}
抽象类
在Java语言中使用abstract 来定义抽象类,其基本语法如下:
abstract class 类名{
//属性
//方法
}
抽象类除了不能实例化对象之外,类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样。
由于抽象类不能实例化对象,所以抽象类必须被继承,才能被使用。在Java中一个类只能继承一个抽象类,但一个类可以实现多个接口。
在Java语言中使用abstract 来定义抽象方法,其基本语法如下:
abstract 访问修饰符 返回值类型 方法名(参数);
抽象类与抽象方法的规则:
- 抽象类不能被实例化(即不能被 new ),只有抽象类的非抽象子类可以创建对象。
- 抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
- 抽象类中的抽象方法只是声明,不包含方法体,也就是不给出方法的具体实现。
- 构造方法、类方法(用 static 修饰的方法)不能声明为抽象方法。
- 抽象类的子类必须给出抽象类中的抽象方法的具体实现,除非该子类也是抽象类。
示例:
Action类:
package com.wmwx.oop.Demo08;
//使用abstract声明抽象类
public abstract class Action {
//抽象方法,只有方法名,没有方法体
//仅作约束,期待他人实现
public abstract void doSomething();
}
A类:
package com.wmwx.oop.Demo08;
public class A extends Action{
//子类必须实现父类的抽象方法
//除非该子类是抽象类
@Override
public void doSomething() {
}
}
接口
接口在JAVA编程语言中是一个抽象类型,是抽象方法的集合,通常以 interface 来声明。其基本语法如下:
[访问修饰符] interface 接口名称 [extends 其他的接口名称] {
// 抽象方法
}
接口的特性:
- 接口中每一个方法都是隐式抽象的,接口中的方法会被隐式地指定为 public abstract,并且只能是 public abstract。
- 接口中可以含有变量,但是接口中的变量会被隐式地指定为 public static final ,并且只能是 public static final。
- 接口中的方法不能在接口中实现,只能由实现接口的类来实现接口中的方法。
- 一个接口能继承另一个接口,使用 extends 关键字.子接口会继承父接口的方法。
当类实现接口的时候,需要实现接口中所有的方法。否则,类必须声明为抽象的类。Java中使用 implements关键字实现接口,其基本语法如下:
class 类名 implements 方法名{
//实现接口中的抽象方法
}
示例:
UserService接口:
package com.wmwx.oop.Demo09;
//定义接口使用关键字interface
public interface UserService {
//接口中的所有属性都是public static final
//一般不在接口中定义属性
int age = 60;
//接口中的所有方法都是public abstract
void add(String name);
void delete(String name);
void update(String name);
void query(String name);
}
TimeService接口:
package com.wmwx.oop.Demo09;
public interface TimeService {
void timer();
}
UserServiceImpl类:
package com.wmwx.oop.Demo09;
//类使用关键字implements来实现接口
//实现了接口的类,需要重写接口的所有方法
//一个类可以实现多个接口
public class UserServiceImpl implements UserService, TimeService{
@Override
public void add(String name) {
}
@Override
public void delete(String name) {
}
@Override
public void update(String name) {
}
@Override
public void query(String name) {
}
@Override
public void timer() {
}
}
内部类
所谓内部类,就是在一个类的内部再定义一个类。
示例:
Outer类:
package com.wmwx.oop.Demo10;
//外部类
public class Outer {
private int id=10;
public void out(){
System.out.println("这是外部类的方法!");
}
public class Inner{
public void in(){
System.out.println("这是内部类的方法!");
}
//获得外部类的私有属性
public int getId(){
return id;
}
}
public void method(){
//局部内部类,在外部类的方法之中
class Inner{
}
}
}
//一个java文件可以有多个class,但是只能有一个public class
class A{
}
Application类:
package com.wmwx.oop;
import com.wmwx.oop.Demo09.UserService;
import com.wmwx.oop.Demo10.Outer;
//启动类
public class Application {
public static void main(String[] args) {
//外部类使用new关键字
Outer outer = new Outer();
outer.out();
//内部类通过外部类来实例化
Outer.Inner inner = outer.new Inner();
inner.in();
System.out.println(inner.getId());
//匿名类,不用将实例保存到变量中
new Outer().out();
//使用匿名类实现接口
UserService userService = new 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) {
}
}
}
}