JavaSE个人学习笔记01:面向对象思想篇

JavaSE个人学习笔记01:面向对象思想篇

前言:计划半个暑假之内系统学习一下JavaSE,现在已经学了一半左右,相信这个目标可以达成。由于本人有C语言的基础,故此笔记略去了基础内容,从Java的特性内容开始记录。开头篇为面向对象思想,内容跳跃性较大、详略不一,如有疑问可参考以下链接中的视频或参考相关书籍或使用搜索引擎等。
欢迎纠错。
教学视频百度云链接 密码:vij4


面向对象思想

万物皆对象
Object类为所有其他类的父类,即上帝、老祖宗


三大特点
* 封装
* 继承
* 多态


封装

隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读和修改的访问级别;将抽象得到的数据和行为(或功能)相结合,形成一个有机的整体,也就是将数据与操作数据的源代码进行有机的结合,形成“类”,其中数据和函数都是类的成员。

数据域的封装:将数据域使用private修饰为私有,并根据情况对外提供访问器(即get方法)和修改器(即set方法)
方法域的封装:对于一些内部使用且不希望向外暴露的方法也可以使用private修饰为私有

构造函数

创建对象时调用的函数且一定会被调用
特点:
1. 函数名与类名相同
2. 没有具体的返回值
3. 无需定义返回值类型
作用:对创建的对象进行初始化
如果一个类没有定义任何的构造函数,那么该类中会有一个默认的空参数构造函数

this关键字

代表当前对象的引用,就是对所在函数所属对象的引用。this也可以用于在构造函数中调用其他的构造函数,但只能定义在构造函数的第一条语句。

static关键字

作用:修饰成员变量和成员函数
被修饰后成员(静态成员)具有以下特点:
* 随着类的加载而加载,随着类的消失而消失
* 优先于对象存在
* 被所有对象所共享,可用于同类对象间共享数据
* 可以直接被类名调用

注意:
* 静态方法只能调用静态成员
* 不可以使用this,super关键字
* main函数为静态函数

成员变量和静态变量的区别:
1. 生命周期不同
* 成员变量是对象所持有的,随着对象的创建而存在,随着对象的被回收而消失
* 静态变量则随着类的加载而存在,随着类的消失而消失
2. 调用方式不同
* 成员变量只能被对象调用。静态变量被对象和类名调用ClassName.staticVariables
3. 别名不同
* 成员变量也称为实例变量,静态变量称为类变量
4. 数据存储位置不同
* 成员变量数据存储在堆内存的实例对象当中,所有叫做对象的特有数据
* 静态变量数据存储与方法区的静态区,所以也叫做对象的共享数据

对于主函数main的分析:

主函数的格式是固定的,被JVM所识别调用
public static void main(String[] args)
public:权限必须最大
static:不需要对象,直接用主函数所属类名调用即可
main:固定的函数名
String[] args:main函数的参数列表,形参命名不固定

何时使用静态:
* 静态变量:在任何对象中数据都相同的变量则修饰为静态
* 静态函数:分析该函数功能是否有访问到对象中的特有数据,如果有则将该函数修饰为静态

静态代码块

随着类的加载而执行,并且只执行一次
作用:给类进行初始化

构造代码块

创建对象是调用,用于初始化对象

执行顺序:静态代码块>mian方法>构造代码块>构造方法
其中静态代码块只执行一次,构造代码块在每次创建对象是都会执行

单例设计模式Singleton

作用:保证在一个JVM中,一个对象只有一个实例,并提供一个访问它的全局访问点。

应用:某些对象创建比较频繁,对于一些大型的对象,这是一笔很大的系统开销。

优点:省去new操作符,可以降低系统内存的使用频率,减轻GC压力。

步骤:
1. 建立私有静态对象,防止被引用
2. 私有化构造方法,不让其他类建立该类对象(防止实例化)
3. 通过getInstance()对外提供对象

/**
 * 懒汉式单例设计模式
 * 特点:线程不安全,调用效率不高,可以延迟加载
 * @classname Singleton1
 * @author LiShengc
 */
public class Singleton1
{
    // 持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载
    private static Singleton1 instance = null;

    // 私有构造方法,防止被实例化
    private Singleton1()
    {
    }

    // 只有调用了getInstance()方法才会创建对象
    public static Singleton1 getInstance()
    {
        if( instance == null )
            instance = new Singleton1();
        return instance;
    }
}
/**
 * 饿汉式单例设计模式
 * @classname Singleton2
 * @author LiShengc
 */
public class Singleton2
{
    // 类被首次加载时,对象就已经被创建存在于内存中
    private static Singleton2 instance = new Singleton2();

    private Singleton2()
    {
    }

    public static Singleton2 getInstance()
    {
        return instance;
    }
}

优缺点比较:
饿汉式比懒汉式常用
1. 饿汉式在加载类时就加载了对象,懒汉式在加载类时对象为空,需要时加载。但是一般用到此方法是都是有对象的。
2. 懒汉式存在安全隐患,当多线程同时访问调用该类是会出现多个对象的可能,需要加入关键字synchronized(保证线程安全),但是加入此关键字会使程序调用变慢。

继承

好处:
- 提高了代码的复用性
- 让类与类之间产生了关系,给多态提供了前提

java中只支持单继承(一个子类只能有一个直接父类),不直接支持多继承(因为多个父类中可能有相同成员,会产生调用不确定性),支持多层继承,但对C++中的多继承机制进行改良(引入接口)。

当需要使用一个继承体系时:
1. 查看该体系中的顶层父类,了解该体系的基本功能
2. 创建体系中的最子类对象,完成功能的使用

当子类的成员和局部变量同名时用this区分子类;当子父类中的成员变量同名时用super区分父类,this区分子类。

super关键字

代表一个父类空间,与this的用法相似。在构造函数中只能在第一条语句中引用。

函数的两个特性

  • 覆盖:override,也称为重写,复写。子类修改父类中已定义的方法,对父类中的方法进行覆盖,也成为方法覆盖

    注意事项:
    1. 子类方法覆盖父类方法是,子类权限必须要大于等于父类的权限
    2. 静态方法只能覆盖父类的静态方法,或被其子类对应的静态方法覆盖

    何时使用覆盖操作?
    当需要对一个类进行子类的功能扩展时,子类需要保留父类的功能声明定义自身中该功能的特有内容时,就使用覆盖完成。

  • 重载:overload。定义不同形参但是重名的方法,这些方法都具有不同的签名,在调用时Java虚拟机JVM会根据不同的实参调用相应的参数。例如不同形参多个构造函数是以重载的方式存在的。

子类的构造函数中第一行有句默认的隐式语句 super();,因此在子类构造对象时,访问子类构造函数时父类的构造函数也运行了,放在第一句是因为父类的初始化动作要优先完成。由于this和super构造语句都只能定义在第一行,因此同一个构造函数中只能调用一个。
子类的实例化过程:子类中所有的构造函数默认都会访问父类中的空参数构造函数。如果父类中没有定义空参数构造函数,那么子类的构造函数必须显式使用super语句,并明确需要调用父类的哪个构造函数。

final关键字

final可以修饰类,方法和变量 * final修饰的类不可继承 * final修饰的方法不可被覆盖 * final修饰的变量是一个常量,只能被赋值一次 * *内部类*只能访问被final修饰的局部变量,用于给常量命名,提高阅读性 * 写法规范:常量名所有字母都大写,多个单词之间使用下划线“_”连接

抽象类(abstract关键字)

特点: * 方法只有声明而没有被实现时,该方法就是抽象方法,需要被abstract修饰 * 抽象方法只能定义在抽象类中 * 抽象类不可以被实例化 * 抽象类必须有其子类覆盖了所有的抽象方法后,该子类才能被实例化,否则这个子类还是抽象类

Q&A
1.Q:抽象类中有构造函数吗?
A:有,用于给子类对象进行初始化。
2.Q:抽象类可以不定义抽象方法吗?
A:可以,但是很少使用,目的是为了不让该类实例化。

/**
 * 抽象类、抽象函数声明示例
 * @classname AbstractDemo
 * @author LiShengc
 */
abstract class AbstractDemo{
    //存在抽象函数,所以此类是抽象类,必须使用abstract修饰
    void function1(){
        //有方法体,非抽象函数
    }
    abstract void function2();//抽象函数,无方法体,必须使用abstract修饰
}

3.Q:abstract关键字不可以和哪些关键字共存?
A:private,final,static

4.Q:抽象类一定是父类吗?
A: 是的,因为需要子类覆盖其方法后才可以对子类实例化进行使用。
5.Q:抽象类和一般类的异同点?
A:相同点:都是用来描述事物,都在内部定义了成员
不同点:
* 一般类具有足够的信息描述事物,抽象类描述事物的信息有可能不足
* 一般类中不可定义抽象方法,只能定义非抽象方法;抽象类中可定义抽象方法和非抽象方法
* 一般类可被实例化,抽象类不能被实例化

接口(interface关键字)

定义格式:`interface InterfaceName{…}` 接口的出现将“多继承”通过“多实现”的形式体现出来。 接口中成员修饰符是固定的: * 成员常量:public static final name; * 成员函数(抽象方法):public abstract function(); 实现接口的子类必须覆盖实现接口中所有的抽象方法后,该子类才能被实例化,否则该类为抽象类。 特点: * 接口时对外暴露的规则 * 程序的功能扩展 * 降低耦合性 * 用来多实现 * 一个接口可以同时继承多个父接口 * 接口与接口之间属于继承关系,类与接口之间属于实现关系(*implements*关键字)。一个类在继承另一个类的同时,还可以实现多个接口(**单继承和多实现**);

若一个子类同时实现存在同名同形参的多个接口成员函数情况下,编译时将会报错,不允许此情况发生,必须避免。在可操作的情况下可以改为接口之间的继承,然后子类对其进行实现。

抽象类与接口的异同点
相同点:都是不断向上抽取而来的
不同点:
1. 抽象类需要被继承,而且只能单继承;
接口需要被实现,可以多实现
2. 抽象类中可以定义非抽象方法,子类继承后可以直接对非抽象方法进行使用;
接口中只能定义抽象方法,必须由子类全部实现,否则该类是抽象类不可被实例化。
3. 抽象类的继承是is a关系,在定义上共享该体系的基本共性内容;
接口的实现是like a关系,用于定义体系的额外功能。

多态

多态性是允许将父对象设置成为一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。在代码中的体现:父类或者接口的引用指向其子类的对象。 好处:提高了代码的扩展性,后期的子类可以使用前期对其父类定义的代码; 弊端:前期实现的父类不能调用后期子类的特有内容。 前提: * 必须要有关系:继承或者实现 * 要有覆盖

转型

子类引用的对象转换为父类类型称为向上转型(赋值引用过程中可自动转型),反过来说,父类引用的对象转换为子类类型称为向下转型(需强制转型)。 向上转型时,父类指向子类引用对象会遗失该子类对象的特有内容(方法及内容) 在向下转型过程中,分为两种情况: * 情况一:如果父类引用的对象如果引用的是指向的子类对象,那么在向下转型的过程中是安全的。也就是编译是不会出错误的。 * 情况二:如果父类引用的对象不是指向的子类对象,那么在向下转型的过程中是不安全的,编译不会出错,但是运行时会出现java.lang.ClassCastException错误。它可以使用*instanceof*来避免出错此类错误。

instanceof关键字

使用方法:`对象名 instanceof 引用数据类型` 返回:如果对象引用的是该数据类型,返回true,否则返回false 用于判断对象的具体类型,只能用于引用数据类型判断,通常在向下转型前用于健壮性的判断。 案例分析:
// 两个子类Student和Worker都继承了Person类
// Student类对父类show方法进行了覆盖;Worker类没有覆盖show方法,但实现了特有方法work。
class Person{
    private String name;// 私有化数据域
    private int age;

    public Person( String name, int age ){
        this.name = name;
        if( age > 0 && age < 120 )// 对输入的数据进行筛选
            this.age = age;
        else
            this.age = 1;
    }
    public void show(){
        System.out.println( name + ":" + age +  " " + Person.show()" );
    }
    // 访问器
    public String getName(){
        return name;
    }
    // 修改器
    public void setName( String name ){
        this.name = name;
    }
    public int getAge(){
        return age;
    }
    public void setAge( int age ){
        this.age = age;
    }
}
/**
 * 继承Person并覆盖父类的show()方法
 * @classname Student
 * @author LiShengc
 */
class Student extends Person{
    public Student( String name, int age ){
        super( name, age );// 引用父类的构造函数
    }
    public void show(){
        System.out.println( "studying..." );
    }
}
/**
 * 继承Person又拥有特有内容(方法work())
 * @classname Worker
 * @author LiShengc
 */
class Worker extends Person{
    public Worker( String name, int age ){
        super( name, age );
    }
    public void work(){
        System.out.println( name + ":" + age +  " " + "working..." );
    }
}
/*
 * 主函数
 */
public class Test{
    public static void main( String[] args ){
        Person person1 = new Student( "小强", 15 );// 向上转型
        Person person2 = new Worker( "大强", 40 );
        function( person1 );
        function( person2 );
    }
    public static void function( Person person ){
        person.show();// 如果子类中覆盖了父类方法,则调用子类方法
        if( person instanceof Student ){// 健壮性判断
            Student student = (Student)person;// 向下转型
            student.show();
        }
        if( person instanceof Worker ){
            Worker worker = (Worker)person;
            worker.work();// 调用子类特有方法
        }
    }
}
输出结果:
studying...
studying...
Person.show()
working...
多态的特点: * 成员函数: 编译时:查看引用变量所属的类中是否有所调用成员 运行时:查看对象所属的类中是否有所调用成员 简单来说,编译看左边,运行看右边。 * 成员变量: 只看引用变量所属的类。简单来说,参考等号左边。 * 静态函数: 无需对象,直接用类名调用即可。

内部类

定义:将一个类定义嵌套在另一个类里,里面的那个类就成为内部类(内置类或嵌套类)。 特点: * 内部类可以直接访问外部类中的所有成员 * 外部类要访问内部类的成员必须要先创建内部类的对象 分析事物时,发现该事物的描述中还有事物,而且这个事物还在访问被描述事物的内容,这是适合用嵌套定义内部类的方式来描述该事物。 如果内部类中定义了静态成员,该内部类也必须是静态的。如果内部类是静态的,相当于一个静态类,可使用 `外部类名.内部类名` 直接引用内部类。

局部内部类

定义在局部位置上的内部类为局部内部类。(不要问我局部是什么,因为我也不太清楚,哪位大神看到帮忙解释下~) 内部类在局部位置上只能访问局部中被final修饰的局部变量。

匿名内部类

前提:该内部类必须继承或者实现一个外部类或者接口,使用父类或实现的接口名对他进行创建,格式: `new 父类|接口(){…}` 。 注意:不要对内部类定义太多函数,会导致阅读性下降;如果匿名内部类创建引用时被向上转型了父类型,则不能使用该子类特有的方法。

包(package)

  • 对类文件进行分类管理
  • 给类提供多层命名空间(用小数点“.”分割层次)
  • 为程序文件的第一条语句
  • 类名的全称是 包名.类名
  • 包也是一种封装形式

public protected (default) private 权限

访问权限 public protected (default) private
同一类中 ok ok ok ok
同一包中 ok ok ok x
子类中 ok ok x x
不同包中 ok × x x








2016/8/21 星期日 上午 by LiShengc

你可能感兴趣的:(JavaSE)