面向对象下:
私有化构造函数:
- 工具类(全都是静态方法或者静态属性)
- 单例模式
单例模式:
某类创建对象只能且最多只有一个对象
将构造函数用private修饰, 让外界无法创建对象
在特殊情况下, 不允许外界创建对象, 减少堆内存占用
步骤:
- 私有化构造函数, 杜绝外界创建对象
- 内部创建该类对象(静态)
- 提供公共接口向外提供该类的对象
饿汉式:
提前提供一个对象
缺点: 即便没有使用对象, 也从始至终常驻内存, 浪费内存
public class Singleton {
private static Singleton singleton = new Singleton;
private Singleton() {
}
public static Singleton getInstance() {
return singleton;
}
}
饱汉式:
访问的才提供对象
缺点: 线程不安全
public class Singleton {
private static Singleton singleton;
private Singleton() {
}
public static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
内部类:
内部类可以直接访问外部类中的成员,但外部类不能直接访问内部类,若要访问,必须创建内部类
对象才能访问
内部类本身就是静态的, 用static修饰后类似于一个外部类了
创建一个内部类和对象:
class Test{
class Inner {
}
}
class Main {
Inner inner = new Test().new Inner();
}
如果用static修饰内部类, 则该内部类属于该外部类, 但不属于外部类的对象
静态内部类可包括静态成员也可包括非静态成员。根据静态成员不能访问非静态成员的规定,所以静态内部类不能访问外部类实例成员,只能访问外部类的静态成员。
class Test2 {
static class Inner2 {
}
}
class Main2 {
Test2.Inner2 inner = new Test2.Inner2();
}
非静态内部类细节
- 注意非静态内部类中不能定义静态成员变量和静态成员函数。但可以定义静态成员常量。原因常量
在生成字节码文件时直接就替换成对应的数字。 - 当内部类在外部类成员位置上被私有修饰,在外部类意外的其他地方是无法访问的。只能在外部类
中访问
以下怎么拿到num:
public class Test {
public static void main(String[] args) {
Outer.Inner in = new Outer().new Inner();
in.show();
}
}
class Outer {
int num = 5;// 外部类的成员变量
class Inner {
int num = 6;// 内部类的成员变量
void show() {
int num = 7; // 内部类局部变量
System.out.println("内部类局部num=" + num);
System.out.println("内部类成员num=" + this.num);
System.out.println("外部类成员num=" + Outer.this.num);
}
注意如果外部类的成员为static, 则内部类在访问外部类成员和方法时用外部类名.来访问
内部类本质上是外部类的成员, 所以外部类的private修饰符对内部类无效
为什么内部类可以直接访问外部类的成员,那时因为内部类持有外部类的引用(外部类.this)。对 于静态内部类不持有 外部类.this 而是直接使用 外部类名。
面向对象三大特征:
- 封装
- 继承
- 多态
有人认为抽象也是一种特征, 但并不被公认
一. 封装(Encapsulation):
为了安全性, 将成员私有化, 不让外界访问, 同时提供特定的访问方式, 相当于是一个外部类
class Cat {
// 首先私有化属性
private int id;
private String name;
private int age;
// 提供对应的公开访问方法
// get方法:
public int getId() {
return this.id;
}
// set方法
public void setId(int id) {
this.id = id;
}
}
POJO对象(Plain Ordinary Java Object)
- 根据封装来写
- 私有化属性
- 提供公开的setter 和 getter 方法
- 至少两个或者以上的构造方法
Java Bean对象:
-
所有的成员变量都要使用 private 关键字进行修饰
-
为每一个成员变量编写 set、get 方法
-
创建一个无参数的构造方法
-
创建一个有参数的构造方法
二. 继承(Inheritance):
在面向对象中, 类与类之间可以存在继承关系
Java是一门典型的单继承编程语言
Java中如何实现继承:
关键字 extends
父类(超类, 基类), 子类
private 修饰的属性和方法都是无法被子类继承的
protected 修饰的方法就是用于子类继承的
class RichMan() {
public int money = 100000000;
private wife = "wife";
private void play() {
System.out.println("炒股");
}
protected void contralCompany() {
System.out.println("掌控公司");
}
}
class Son extends RichMan() {
public static void main(String[] args) {
Son son = new SOn(); // son中的成员只有money, 方法只有contralCompany
System.out.println(son.money);
son.contralCompany();
}
}
继承的功能:
减少代码的重复, 提高代码的复用度
方法重写(覆盖):
重写(OverWrite)
覆盖(OverRide)
发生在继承中, 指的是, 子类继承了父类方法后不满足使用, 就重写该方法以满足子类使用
重写是访问修饰符权限可以放大但不能缩小, 除了访问修饰符, 返回值和参数还有函数名多保持一致
注解:
@override // 覆盖
@Deprected // 过时
@SuppressWarning // 压制警告
class RichMan() {
public int money = 100000000;
private wife = "wife";
public void play() {
System.out.println("炒股");
}
}
class Son extends RichMan() {
@Override
public void play() {
System.out.println("健身");
}
public static void main(String[] args) {
Son son = new SOn();
son.play();
}
}
super关键字:
super 在java中, 是一个指针, 类似于this关键字
this关键字指向创建的每一个对象
super会自动指向父类
super( ); // 调用父类的无参构造
final关键字:
final : 最后
-
被他修饰的变量就是常量
static final
-
用final修饰的方法不能被重写
-
final关键字修饰类则该类不能被继承
为什么类用final修饰:
基于jvm的安全性考虑, 防止被继承之后底层代码被篡改从而引起各种安全问题
对象的创建流程及内存结构三:
- java命令将源码(.java)进行编译, 生成字节码文件(.class)
- javac命令执行字节码文件
- 将字节码文件加载进虚拟机, 具体方法进入方法区(非静态区和静态区)
- JVM从静态区中寻找main函数, 并将其加载进栈开始执行程序
- main 函数开始执行, 创建对象代码, 如Son son = new Son();
- 在堆内存中开辟对对象的内存空间, 并分配地址
- 创建成员变量开始初始化
- 子类构造函数从非静态方法区加载进栈开始执行
- 第一句先执行父类的构造函数
- 父类构造函数执行, 为子类继承到的成员变量进行初始化(对象内存空间里的父类空间)
- 父类构造函数弹栈执行完成
- 子类构造函数继续执行
- 再执行子类构造函数的内容, 进行针对性初始化
- 执行完成, 子类构造函数弹栈, 将对象的内存空间地址赋予相应的引用变量
instanceof 关键字 判断对象是否为对应类, 配合继承判断
三. 多态(Polymorphism):
在继承的基础上才有多态
父类应用指向子类示例
Animal cat = new Cat( );
访问不到子类自身的方法, 但可以重写父类方法来满足子类使用
多态的好处
提高了程序的扩展性。
多态的弊端
通过父类引用操作子类对象时,只能使用父类中已有的方法,不能操作子类特有的方法。
多态的前提
\1. 必须有关系:继承,实现。 2. 通常都有重写操作
当父类的引用指向子类对象时,就发生了向上转型,即把子类类型对象转成了父类类型。向上转型
的好处是隐藏了子类类型,提高了代码的扩展性。
public class Test {
public static void main(String[] args) {
Animal cat = new Cat("小黄", "猫");
Animal dog = new Dog("小红", "狗");
PetStore store = new PetStore();
store.wash(cat);
store.wash(dog);
}
}
@Setter
@Getter
class Animal {
String name;
String type;
public Animal() {
}
public Animal(String name, String type) {
this.name = name;
this.type = type;
}
public void beWash() {
// 这段函数体没意义只用来被重写, 可以用抽象方法从而省去写这个函数
}
}
class Dog extends Animal {
public Dog() {
}
public Dog(String name, String type) {
super(name, type);
}
@Override
public void beWash() {
System.out.printf("%s正在被洗, 是一只%s\n", this.name, this.type);
}
}
class Cat extends Animal {
public Cat() {
}
public Cat(String name, String type) {
super(name, type);
}
@Override
public void beWash() {
System.out.printf("%s正在被洗, 是一只%s\n", this.name, this.type);
}
}
class PetStore {
public void wash(Animal pet) {
pet.beWash();
}
}
抽象:
抽象方法:
如果一个方法, 不需要实现体(函数内容), 就可以声明抽象方法
抽象方法: 没有方法体的方法, java中使用abstract关键字声明的方法
访问修饰符 abstract 返回值类型 方法名称
抽象方法必须写在抽象类中
抽象类:
被abstract关键字声明的类
访问修饰符 abstract class 类名
抽象父类不写抽象方法, 则子类需要写, 但抽象子类可以不写, 留给子类以后的孙类解决
注意: 抽象类中不一定有抽象方法, 有抽象方法就肯定是抽象类
如果一个类继承了抽象类, 就必须实现抽象方法, 如果一定不实现, 则这个子类也声明为抽象类
抽象类不可以创建示例, 原因: 调用抽象方法没有意义
存在的意义是为了继承给子类, 通过多态调用子类对抽象方法的实现
抽象关键字abstract不可以和哪些关键字共存?
- final: fianl修饰的类是无法被继承的, 而abstract修饰的类一定要有子类. final修饰的方法无法 被覆盖, 但是abstract修饰的方法必须要被子类去实现的
- static: 静态修饰的方法属于类的, 它存在与静态区中, 和对象就没关系了. 而抽象方法没有 方法体, 使用类名调用它没有任何意义
- private: 私有的方法子类是无法继承到的, 也不存在覆盖, 而abstract和private一起使用修饰 方法, abstract既要子类去实现这个方法, 而private修饰子类根本无法得到父类这个方法. 互相矛盾
接口:
接口本质是特殊的类, 特殊抽象类
接口中所有方法都是没有实现的(抽象方法)
在jdk8之前: 接口中的所有属性方法, 默认都是public的, 即使没有用public修饰
java中使用interface关键字来定义接口
接口无法示例化, 必须使用子类, 以多态的形式完成实现
java中, 接口可以多实现多继承
接口的定义和实现:
public interface TestInterface {
public void say();
void play();
}
public class MyInterface implements TestInterface {
@Override
public void say() {
// 代码
}
@Override
public void play() {
// 代码
}
}
在jdk8中接口可以定义普通方法, 但也只能通过多态的方式访问, 用default关键字修饰
public interface Test {
void show();
public default void func() {
System.out.println("执行");
}
}
也可定义静态方法, 相当于把接口做了工具类
public interface Test {
void show();
public static void func2() {
System.out.println("执行");
}
}
接口多实现多继承:
interface A {
void show1();
}
interface B {
void show2();
}
// 接口多实现
class test implements A, B {
public void show1() {
}
public void show2() {
}
}
// 接口多继承
interface C extends A, B {
void show3();
}
接口与抽象类的区别
相同点:
- 都位于继承的顶端,用于被其他实现或继承
- 都不能实例化
- 都包含抽象方法,其子类都必须覆写这些抽象方法
区别:
- 抽象类为部分方法提供实现,避免子类重复实现这些方法,提供代码重用性
- 接口只能包含抽象方 法; 一个类只能继承一个直接父类(可能是抽象类),却可以实现多个接口(接口弥补了Java的单继承)
二者的选用:
- 优先选用接口,尽量少用抽象类
- 需要定义子类的行为,又要为子类提供共性功能时才选用抽象类
静态导包:
针对于静态方法, 导入成功可以直接使用静态方法而不需要通过类名.的形式访问
例如:
import static java.util.Arrays.sort;
class Test {
public static void main(String[] args) {
int[] arr = {1, 2, 34, 1, 2, 5, 76, 4};
sort(arr);
}
}