面向对象
1.什么是面向对象
- 面向对象编程OOP
- 本质:以类的方式组织代码,以对象的组织(封装)数据
- 抽象
- 三大特征:封装、继承、多态
2. 类与对象关系
- 类是一种抽象的数据类型,它是对某一类事务整体的描述/定义,但是并不能代表一个具体的事务
- 比如Person类代表所有的人,Car类代表所有的车,这些都是用来描述或定义某一类具体的事务应该具备的特点和行为
- 对象是抽象概念的具体实例
- 比如张三是Person类的具体实例,他开的宝马就是Car类的具体事例
3.创建与初始化对象
3.1 使用new关键字创建对象
使用new关键字创建的时候,出了分配内存空间之外,还会给创建好的对象进行默认的初始化以及对类中构造器的调用
测试案例:
Student:
//学生类
public class Student {
//属性:字段
String name;
Integer age;
//方法
public void study(){
System.out.println(this.name+"正在学习");
}
}
Test:
/**
* 测试类
* 一个项目只应该存在一个main方法
*/
public class Test {
public static void main(String[] args) {
//类是抽象的,需要实例化
//类实例化后,会返回一个自己的对象
Student student = new Student();
student.name = "张三";
student.age = 18;
System.out.println(student.name);
System.out.println(student.age);
student.study();
}
}
运行结果:
张三
18
张三正在学习
3.2 构造器
类中的构造器也称为构造方法,是在进行创建对象的时候必须要调用的,构造器的两个特点:
- 必须和类的名字相同
- 必须没有返回类型,也不能写void
作用:
- new本质在调用构造方法
- 初始化对象的值
注意点:
- 定义有参构造之后,如果想使用无参构造,要显示的定义一个无参构造
测试案例:
Person
public class Person {
String name;
/**
* 一个类即使什么都不写 也会存在一个无参构造器
* 实例化初始值
* 1.使用new关键字,本质是在调用构造器
* 2.用来初始化
*/
// 显示定义构造器,这是一个默认构造器
public Person(){
}
/**
* 有参构造
* 一旦定义了有参构造,无参构造就必须显示定义
*/
//相当于重载
public Person(String name){
this.name = name;
}
}
Test
public class Test {
public static void main(String[] args) {
Person person = new Person(); //调用无参构造
Person person1 = new Person("张三"); //调用有参构造
System.out.println(person.name);
System.out.println(person1.name);
}
}
运行结果:
null
张三
4. 封装
-
封装(属性的隐藏)
- 通常,应禁止直接访问一个对象中数据的实际显示,而应通过操作接口来访问,这称为信息隐藏
-
程序设计要追求“高内聚,低耦合”。
- 高内聚:类的内部数据操作细节自己完成,不允许外部干涉。
- 低耦合:仅暴露少量的方法给外部使用
-
属性私有,get/set
优点:
- 提高程序的安全性,保护数据
- 隐藏代码的实现细节
- 统一接口
- 提高了系统的可维护性
Student
/**
* 类 private:私有
*/
public class Student {
//属性私有
private String name;
private Integer id;
private String sex;
private Integer age;
//提供一些方法可以操作这个属性的方法
//get 获取值 set 设置值
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
if (age > 120 || age < 0) { //不合法
this.age = age;
}else {
age = 18;
}
}
}
Test
public class Test {
public static void main(String[] args) {
Student student = new Student();
student.setName("张三");
student.setId(1);
student.setSex("男");
student.setAge(130);
System.out.println(student.getName());
System.out.println(student.getAge());
}
}
运行结果:
张三
18
5. 继承
5.1 继承的理解
-
继承的本职是对某一批类的抽象,从而实现对现实世界更好的建模。
-
extends 意思是扩展。子类是父类的扩展。
-
java中类只有单继承,没有多继承!
-
继承是类与类之间的关系,还有依赖、组合、聚合等。
-
继承关系的两个类,一个为子类(派生类),一个为父类(基类)。子类继承父类用extends来表示。
-
Java中,所有的类都默认直接或简洁继承Object类
Person
//人:父类
public class Person {
public void people(){
System.out.println("大家都是人类");
}
}
Chinese
//中国人:子类
public class Chinese extends Person{
}
Test
public class Test {
public static void main(String[] args) {
//子类继承的父类,就会有他的全部方法
Chinese chinese = new Chinese();
chinese.people();//大家都是人类
}
}
5.2 super详解
Person
//人:父类
public class Person {
public Person() {
System.out.println("person无参执行了");
}
protected String name = "张三";
public void print(){
System.out.println("person");
}
}
Chinese
//中国人:子类
public class Chinese extends Person{
private String name = "李四";
public Chinese() {
/**
* super();隐藏代码,调用了父类的无参构造
* 必须要在子类的第一行
*/
System.out.println("chinese无参执行了");
}
public void print(){
System.out.println("chinese");
}
public void test(String name){
System.out.println(name); //王五
System.out.println(this.name); //李四
System.out.println(super.name); //张三
}
public void test1() {
print(); //chinese
this.print(); //chinese
super.print(); //person
}
}
Test
public class Test {
public static void main(String[] args) {
Chinese chinese = new Chinese();
System.out.println("=============");
chinese.test("王五");
System.out.println("=============");
chinese.test1();
}
}
运行结果:
person无参执行了
chinese无参执行了
=============
王五
李四
张三
=============
chinese
chinese
person
总结:
super注意点:
- super调用父类的构造方法,必须放在构造方法第一个
- super必须只能出现在子类的方法或者构造方法中
- super和this不能同时调用构造方法
super 和 this:
- 代表对象不同:
- this:本身调用者这个对象
- super:代表父类对象的引用
- 前提:
- this:没有继承也可以使用
- super:只能在继承条件下才能使用
- 构造方法区别:
- this(); 本类的构造
- super(); 父类的构造
5.3 方法重写
- 需要有继承关系,子类重写父类的方法!
- 方法名必须相同
- 参数列表必须相同
- 修饰符:范围可以扩大,但不能缩小(public>protected>default>privite)
- 抛出的异常:范围可以被缩小,但不能扩大
- 重写,子类的方法和父类必须要一直;方法体不同!
- 为什么需要重写?
- 父类的功能,子类不一定需要,或者不一定满足
A
//继承B
public class A extends B{
//重写
@Override
public void test(){
System.out.println("A");
}
}
B
//重写都是方法的重写,和属性无关
public class B {
public void test(){
System.out.println("B");
}
}
Test
public class Test {
public static void main(String[] args) {
//静态方法:方法的调用只和左边的定义的数据类型有关
//非静态:可以选择重写
A a = new A();
a.test();
//父类的引用指向了子类
B b = new A();//子类重写了父类的方法
b.test();
}
}
运行结果:
A
A
6. 多态
6.1 多态的理解
- 同一方法可以根据发送对象的不同而采取多种不同的行为方式
- 一个对象的实际类型是确定的,但可以指向对象的引用类型有很多
- 多态存在的条件:
- 有继承关系
- 子类重写父类方法
- 父类引用指向子类对象
- 注意:多态是方法的多态,属性没有多态!
Person
public class Person {
public void run(){
System.out.println("person");
}
}
Student
public class Student extends Person{
@Override
public void run() {
System.out.println("student");
}
public void eat(){
System.out.println("eat");
}
}
Test
public class Test {
public static void main(String[] args) {
/**
* 一个对象的实际类型是确定的
* new Student();
* new Person();
*/
//可以指向的引用类型就不确定了:父类的引用指向子类
//Student能调用的方法都是自己的或者继承父类的
Student s1 = new Student();
//Person可以指向子类,凡是不能调用子类独有的方法
Person s2 = new Student();
Object s3 = new Student();
s2.run();//子类重写了父类的方法,执行子类的方法
s1.run();
//对象能执行哪些方法,主要看对象左边的类型,右边的关系不大
s1.eat();
// s2.eat();
((Student)s2).eat();//强转
}
}
运行结果:
student
student
eat
eat
注意事项:
- 多态是方法的多态,属性没有堕胎
- 父类和子类,有联系 没联系类型会转换异常!ClassCastException
- 存在条件:继承关系,方法需要重写,父类的引用指向子类对象!
不能重写的情况:
-
static方法,属于类,他不属于实例
-
final是常量,final修饰的不能重写
-
private方法:私有的,不能重写
6.2 instanceof关键字和类型转换
- instanceof(类型转换),引用类型,判断一个对象是什么类型
//建一个Student类和Teacher类继承Person类
public class Test {
public static void main(String[] args) {
//Object > Person > Student
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
//Object > Person > Teacher
System.out.println(object instanceof Teacher);//false
//Object > String
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
public class Person {
public void run(){
System.out.println("person");
}
}
//Student
public class Student extends Person {
public void go() {
System.out.println("student");
}
}
//Test
public class Test {
public static void main(String[] args) {
//类型转换
Person obj = new Student();
//student将这个对象转化为Student类型,就可以使用Student类型的方法了
//person.go();
((Student)obj).go();//输出:student
}
}
7. Static关键字
非静态方法可以调用非静态方法,静态类会直接加载
Student
public class Student {
private static int age;//静态变量
private double score;//非静态变量
public void run(){
go();//非静态方法可以调用非静态方法
}
public static void go(){
}
public static void main(String[] args) {
Student s1 = new Student();
System.out.println(Student.age);
// System.out.println(Student.score); 报错
System.out.println(s1.age);
System.out.println(s1.score);
new Student().run();
go();
}
}
Person
public class Person {
//赋初始值
{
System.out.println("匿名代码块");
}
//只执行一次
static {
System.out.println("静态代码块");
}
public Person() {
System.out.println("构造方法");
}
public static void main(String[] args) {
Person person1 = new Person();
System.out.println(person1);
System.out.println("====================");
Person person2 = new Person();
System.out.println(person2);
}
}
/**
静态代码块
匿名代码块
构造方法
com.banyuan.demo07.Person@7c30a502
====================
匿名代码块
构造方法
com.banyuan.demo07.Person@49e4cb85
*/
Test(新特性)
//静态导入包
import static java.lang.Math.random;
import static java.lang.Math.PI;
public class Test {
public static void main(String[] args) {
System.out.println(random());
System.out.println(PI);
}
}
8. 抽象类
- abstract修饰的方法为抽象方法,修饰的类为抽象类
- 抽象类中可以没有抽象方法,但是有抽象方法的类一点要声明为抽象类
- 抽象类,不能使用new关键字来创建对象,它是用来让子类继承的
- 抽象方法,只有方法的声明,没有方法的实现,它是用来让子类实现的
- 子类继承抽象类,那么就必须要实现抽象类没有实现的抽象方法,否则该子类也要声明为抽象类
B
public abstract class B {
//约束 有人帮我们实现
//抽象方法,只有方法名字,没有方法的实现
public abstract void A();
/**
* 1.不能new这个对象类,只能靠子类去实现它;约束!
* 2.抽象类中可以写普通的方法
* 3.抽象方法必须写在抽象类中
*/
}
A
//抽象类的所有方法,继承了它的子类,都必须要实现他的方法,除非子类本身也是abstract
public class A extends B {
@Override
public void A() {
}
}
9. 接口
普通类:只有具体实现
抽象类:具体实现和规范(抽象方法)都有
接口:只有规范
- 关键字:interface
UserService
//关键字:interface,接口都需要实现类
public interface UserService {
//接口中的所有定义其实都是抽象的 默认:public abstract
void add(String name);
void delete(String name);
void update(String name);
void query(String name);
//常量 默认:public static final 接口中一般不写常量
Integer age = 99;
}
TimeService
public interface TimeService {
void timer();
}
UserServiceImpl
//实现类
//实现了接口的类,就需要重写接口中的方法
//多实现,java只有单继承但是可以用接口多实现
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() {
}
}
总结:
- 作用:
- 约束!
- 定义一些方法,让不同的人实现
- 方法默认都是 public abstract 常量默认 public static final
- 接口不能被实例化,接口中没有构造方法
- 接口可以实现多个
- 实现接口必须要重写接口中的方法
10. 内部类
- 内部类就是在一个类的内部再定义一个类,比如A类中定义一个B类,那么B类相对A类来说就是内部类
- 成员内部类
Outer
public class Outer {
private Integer id = 10;
public void out() {
System.out.println("这是外部类的方法");
}
public class Inner {
public void in() {
System.out.println("这是内部类的方法");
}
//获得外部类的私有属性
public void getID() {
System.out.println(id);
}
public void getOut(){
Outer.this.out();
}
}
}
Test
public class Test {
public static void main(String[] args) {
Outer outer = new Outer();
//通过外部类来实例化内部类,成员内部类
Outer.Inner inner = outer.new Inner();
inner.in();
inner.getID();
inner.getOut();
}
}
结果:
这是内部类的方法
10
这是外部类的方法
- 静态内部类
public class Outer {
private Integer id = 10;
public void out() {
System.out.println("这是外部类的方法");
}
public static class Inner {
public void in() {
System.out.println("这是内部类的方法");
}
}
无法调用外部的id,因为static后比id先执行
- 局部内部类
public class Outer1 {
//局部内部类
public void B(){
class C{
}
}
}
- 匿名内部类
public class Outer2 {
public static void main(String[] args) {
//没有名字初始化类,不用将实例保存到变量中
new Apple().eat();
//实现了接口的类
new UserService(){
@Override
public void hello() {
}
};
}
}
class Apple {
public void eat(){
System.out.println("吃苹果");
}
}
interface UserService{
void hello();
}