面向对象笔记总结,随时查询
面向对象
类和对象
类是抽象的,是一个模板,确定对象将会拥有的特征(属性)和行为(方法)
对象是实体,是类的实例表现
属性和方法
属性:对象具有的各种静态特征
方法:对象具有的各种动态行为
命名
包的命名为域名小写字母的倒序加上功能名
类名首字母要大写
实例化对象
12345678 | public class { String name; public void run() { System.out.println("run"); }} |
12345 | public static void main(String[] args) { Cat one = new Cat(); // 声明并实例化对象 System.out.println(one.name); // 获取类中的对象 one.run(); // 获取类中的方法} |
new
每次new都会开辟一个新空间,不会相互影响
123 | Cat one = new Cat(); Cat two = one; // 此时one和two都指向通过一个堆内地址,会相互影响 |
构造方法
- 构造方法与类同名但是没有返回值
- 在对象实例化的时候调用
- 一个类中可以有多个构造方法
无参构造方法
不写构造方法,系统默认创建一个无参构造方法,自己创建带参构造方法,系统将再自动创建无参构造方法,需要自己手动创建
带参构造方法
将参数传递给类,进行属性的赋值
this
在构造函数中,可以将参数赋值给类中的属性,解决不能重名的问题
谁调用,this
就指向谁(one调用,所以this执行one)
123 | Cat(String name) { this.name = name;} |
在类中也可以通过this
,在方法中调用另一个方法
将this()
写在第一行可以在带参构造函数前,调用无参构造函数
封装
只能通过规定的方法访问
隐藏类的实例细节,方便修改和实现
实现步骤
- 将属性的可见性修改为
private
- 添加
get/set
方法 - 可以在
get/set
方法中添加过滤信息,如果使用了过滤信息,可以在构造函数中使用set
进行赋值操作,避免传入参数类型有误
static
- 类中的属性设置
static
,通过这个类创建的所有对象将共用这个属性 - 类中的静态成员,可以通过对象名或类名访问,推荐使用类名访问
- 静态方法中不能直接访问非静态成员(属性和方法),只能调用静态成员,除非实例化一个对象,通过对象调用,子类无法重写静态方法
- 普通方法可以直接访问类中的静态方法
- 普通代码块,方法之间的代码块,顺序执行
- 类中的代码块叫构造代码块,构造代码块将在构造方法之前执行,多个构造代码块按顺序执行
- 静态代码块,就是在构造代码块前添加
static
,静态代码块优先于构造代码块执行 - 静态代码块只会在类加载时执行一次
继承
继承是一种类与类之间的关系
使用已存在的类作为基础建立新的类
新类可以增加新的数据或新的功能,也可以用父类的功能,但是不可以选择继承父类中的属性或功能
继承的实现
子类可以调用父类中的方法使用父类中的方法,父类不可以使用子类
12 | public class Animal { // 父类} |
12 | public class extends Animal { // 子类} |
方法重载和方法重写
子类可以重写父类中的方法
方法重载:
- 同一个类中
- 方法名相同,参数类表不同(参数顺序、个数、类型)
- 方法返回值和访问修饰符任意
- 与方法参数名无关
方法重写:
- 有继承关系的子类中
- 方法名相同,参数类表相同(参数顺序、个数、类型),方法返回值相同(如果父类返回值是父类类型Animal,子类返回值是子类类型Cat,也可以)
- 访问修饰符,访问范围要大于等于父类访问修饰符
- 与方法参数名无关
访问修饰符
- private 只允许在本类中访问
- public 允许在任意位置访问
- protected 允许在当前类,同包子类/非子类、跨包子类调用调用,跨包非子类不允许
- 默认 允许在当前类,同包子类/非子类调用,跨包子类/非子类不允许
super
可以通过super.xxx在子类中调用父类的属性或方法
子类构造函数默认调用父类的无参构造,可以通过super()
调用父类的中允许被访问的其他构造
super()
必须放在子类构造方法有效代码第一行
super和this
- 都不可以在静态方法中使用
- this是调用当前类中的属性或方法,super是调用父类中的属性或方法
- super()和this()只能出现一个,因为都是要在代码第一行才有效
继承初始化顺序
父类静态成员-》子类静态成员-》父类对象构造-》子类对象构造
Object
equals()
- 继承Object中的equals方法时,比较两个引用是否指向同一个对象
- 子类可以通过重写equals方法的形式,改变比较内容,String中默认重写该方法,比较值是否相同
toString()
- 输出对象名,默认调用toString
- 继承Object中的toString方法时,输出对象的字符串表示形式:类型信息+@+地址信息
- 子类可以通过重写toString方法的形式,改变输出内容,String中默认重写该方法,输出字符串
final
为类添加final
,表示不能被继承
123 | public final class Animal {} // 第一种final public class Animal {} // 第二种 |
为方法添加fina
l,表示不能被子类重写,但可以正常被子类调用
为类中的属性添加final
,只有在定义时、构造方法中、构造代码块中才能进行赋值
为基本数据类型添加final
,表示在使用前要先赋值,一旦赋值不可修改
为引用数据类型添加final
,表示不能修改引用对象,但可以修改引用对象的值
配合static
使用表示在全局中只加载一次且不可修改,可以用在配置文件
1 | public static final String URL = "http://xxx"; |
注解
- 源码注解 -》 只有源码中存在的注解,给编译器看,编译成class文件不存在
- 编译时注解 -》 编程成class文件,依然存在的注解
- 运行时注解 -》 在运行时会产生作用的注解
通过@Override
注解表示重写父类中的方法
单例模式
- 只提供私有的构造方法
- 含有一个该类的静态私有对象
- 提供一个静态的公有方法用于创建、获取静态私有对象
饿汉式
创建对象实例的时候直接初始化,所以调用时速度快,但是因为在创建时已经创建,但是比较占据空间,就是用空间换取时间
123456789101112 | public class SingletonOne { // 1.创建类中私有构造 private SingletonOne() {} // 2.创建该类型的私有静态实例 private static SingletonOne instance = new SingletonOne(); // 3.创建共有静态方法返回静态实例对象 public static SingletonOne getInstance() { return instance; }} |
懒汉式
类内实例对象创建时并不直接初始化,知道第一次调用get方法时,才完成初始化操作,用时间来换取空间
123456789101112131415 | public class SingletonTwo { // 1.创建类中私有构造 private SingletonTwo() {} // 2.创建该类型的私有静态实例 private static SingletonTwo instance = null; // 3.创建共有静态方法返回静态实例对象 public static SingletonTwo getInstance() { if (instance == null) { instance = new SingletonTwo(); } return instance; }} |
使用场景
- 创建对象时占用资源过多,但同时又需要用到该类对象
- 对系统内资源要求统一读写,如配置信息
- 当多个实例存在可能引起程序逻辑错误,如号码生成器
多态
- 满足继承关系
- 父类引用指向子类对象
向上转型
向上转型、隐式转型、自动转型 -> 父类引用子类实例(小类转向大类)
可以调用子类重写父类的方法已经父类的派生的方法,无法调用子类独有的方法
注:父类中的静态方法无法被子类重写(static方法无法被子类重写),所以向上转型后,只能调用父类原有的静态方法
12 | Animal two = new Cat();Animal three = new Dog(); |
向下转型
向下转型、强制类型转换
子类引用指向父类对象,此处必须进行强转,可以调用子类特有的方法
必须满足转换条件才能强转,使用instanceof
进行判断是否满足强转条件
123 | Cat temp = (Cat)two;temp.run();temp.eat(); |
1234567891011 | if (two instanceof Cat) { Cat temp = (Cat)two; temp.run(); temp.eat();}if (two instanceof Dog) { Dog temp2 = (Dog)two; temp2.sleep(); temp2.eat();} |
类型转换实例
12345 | Master master = new Master();Cat cat = new Cat();Dog dog = new Dog();master.feed(cat);master.feed(dog); |
方法一、根据传入不同的类型,调用不同的方法
123456789 | public void feed(Cat cat) { cat.eat(); cat.playBall();}public void feed(Dog dog) { dog.eat(); dog.playBall();} |
方法二、传入动物的父类,方法中通过类型转换,调用子类的方法
1234567891011 | public void feed(Animal obj) { if (obj instanceof Cat) { Cat temp = (Cat)obj; temp.eat(); temp.playBall(); } else if (obj instanceof Dog) { Dog temp = (Dog)obj; temp.eat(); temp.playBall(); }} |
抽象类
不允许实例化,可以通过向上转型,指向子类实例
父类只知道子类应该包含怎样的方法,但无法准确知道这些子类如何实现这些方法,可以使用抽象类
1 | public abstract class Animal {} |
抽象方法
不允许包含方法体,子类中需要重写父类的抽象方法,否则,子类必须是抽象类
抽象方法要写在抽象类中
static、final、private不允许和abstract并存
1 | public abstract void eat(); |
接口
接口定义了类需要遵守的规范
接口不关系类的内部数据,也不关系类中方法实现的细节,只规定类中必须提供某些方法
习惯将接口命名以大写I
打头
1234 | IPhoto ip = new FourthPhone();ip.photo();ip = new Camera();ip.photo(); |
123456789 | public interface IPhoto { // 接口中的抽象方法可以省略abstract // 默认访问权限为public,可以省略 void photo(); // 接口中的常量可以省略public static final // 接口中常量和实现类中常量名相同,使用接口引用指向实现类,则调用的是接口中的常量 int TEMP = 20;} |
123456 | public class Camera implements IPhoto { public void photo() { System.out.println("相机拍照"); }} |
123456 | public class FourthPhone implements IPhoto { public void photo() { System.out.println("手机拍照"); }} |
jdk1.8后可以在接口中写默认方法和静态方法
1234567 | default void connection() { // 在调用接口的类中不是必须被实现,可以被重写 System.out.println("默认连接");}static void stop() { // 通过接口.stop调用 System.out.println("接口静态方法");} |
如果接口中方法名相同,那么实现类中要重写该方法
如果接口和父类方法名相同,默认调用父类方法,无需重写(常量父类不会默认调用,要在实现类中进行实现)
接口可以多继承
内部类
成员内部类
- 内部类在外部使用时,无法直接实例化,需要借由外部类信息才能完成实例化
- 内部类的访问修饰符,可以任意,但是访问范围会受到影响
- 内部类可以直接访问外部类的成员,如果同名,优先访问内部类中定义的
- 可以使用外部类.this.成员的方式,访问外部类中的同名的信息
- 外部类访问内部类信息,需要通过内部类实例,无法直接访问
12345678910111213141516 | public static void main(String[] args) { Person lili = new Person(); lili.age = 20; // 1. new 外部类.new 内部类 Person.Heart myHeart = new Person().new Heart(); System.out.println(myHeart.beat()); // 2. 外部类对象.new 内部类 myHeart = lili.new Heart(); System.out.println(myHeart.beat()); // 3. 外部类对象.获取方法 myHeart = lili.getHeart(); System.out.println(myHeart.beat());} |
12345678910111213141516171819 | public class Person { public int age; public Heart getHeart() { return new Heart(); } public void eat() { System.out.println("eat"); } public class Heart { int age = 13; public String beat() { eat(); return age + "跳动"; } }} |
静态内部类
静态内部类中,只能直接访问外部类的静态成员,如果需要调用非静态成员,可以通过对象实例
静态内部类对象实例时,可以不依赖于外部类对象
可以通过外部类.内部类.静态成员的方式,访问内部类中的静态成员
当内部类属性与外部类属性同名时,默认直接调用内部类中的成员
如果需要访问外部类中的静态属性,可以通过 外部类.属性 的方式
如果需要访问外部类中的非静态属性,可以通过 new 外部类().属性 的方式
12345 | Person.Heart myHeart = new Person.Heart();System.out.println(myHeart.beat());Person.Heart.say();System.out.println(Person.Heart.age); |
123456789101112 | public static class Heart { static int age = 13; public static void say() { System.out.println("hello"); } public String beat() { new Person().eat(); return Person.age + "跳动"; }} |
方法内部类
- 定义在方法内部,作用范围也在方法内
- 和方法内部成员一样,
class
前不可以添加public
、priivate
、protected
、static
- 类中不能包含静态成员
- 类中可以包含
final
、abstract
修饰的成员
123456789101112131415 | public Object getHeart() { class Heart { int age = 13; public void say() { System.out.println("hello"); } public String beat() { new Person().eat(); return Person.age + "跳动"; } } return new Heart().beat();} |
匿名内部类
适用场景:
只用到类的一个实例
类定义后马上使用
给类命名并不会导致代码更容易理解
总结:
- 匿名内部类没有类型名称、实例对象名称
- 无法使用private、public、protected、abstract、static修饰
- 无法编写构造方法,可以添加构造代码块
- 不能出现静态成员
- 匿名内部类可以实现接口也可以继承父类,但是不可兼得
12345678910111213141516171819202122 | public class PersonTest { public void getRead(Person person) { person.read(); } public static void main(String[] args) { PersonTest test = new PersonTest(); test.getRead(new Person(){ public void read() { System.out.println("man"); } }); test.getRead(new Person(){ public void read() { System.out.println("woman"); } }); }} |
12345678910111213141516 | public abstract class Person { private String name; public Person() { } public String getName() { return name; } public void setName(String name) { this.name = name; } public abstract void read();} |