Java&OOP

Java OOP

OOP

类型

接口:定义需要具有哪些属性

类:比较复杂的结构、存储同一个类型的多个变量

  • 封装起来不同的属性,都扔到一个桶里面
  • 规定:记录下来的属性有啥&能干啥
  • 实例化:变成一个东西
    • 构造函数里面可以进行初始化
    • 没有初始化的:基本类型设置为默认值,类的设置为null
    • 从模具到东西
  • object:对象
    • 内存一片区域
    • 分成几个部分,里面分别装着属性值(instance variables,实例变量,每个object都有自己的)
      • 加上static之后就是类变量
    • 通过.来得到他的实例变量引用,可以直接进行操作
      • 解引用,de-reference
  • 方法:
    • 构造函数(上面实例化)
    • accessor method:访问方法,只访问不修改 getter
    • update method:更新方法,进行修改setter

多态性

- 重载:overloading,参数不同,方法名相同,一个类的多态性
  - ![image-20200227093906231](Java OOP&数据结构.assets/image-20200227093906231.png)
- 重写:overriding,方法名参数都相同,父类子类
- ![img](Java OOP&数据结构.assets/overloading-vs-overriding.png)

引用类型

  • java通过引用操作自己的对象

  • new函数创建一个对象,把引用给了变量(引用变量),class是引用类型

    • 在heap堆上开一片地方放着

    • Heap内存用于存放由new创建的对象和数组。在中分配的内存,由java虚拟机自动垃圾回收器来管理。

  • Java默认new 对象则为强引用,如

    1
    >StringBuffer buffer = new StringBuffer();

    上面创建了一个StringBuffer对象,并将这个对象的强引用存到变量buffer中。如果一个对象通过一串强引用链接可到达,即使内存不足,也不会回收该对象。

    作者:挨踢小能手
    链接:https://www.jianshu.com/p/c54d1f00ca5f
    来源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

NULL

  • 不赋值默认为null,不同类型的null相等

  • 不能转换为null,不能设置null类型

  • 不能解引用

1定义

null 是所有引用类型的默认值。

2. 转换

null既不是对象也不是一种类型,它仅是一种特殊的值,你可以将其赋予任何引用类型,它还仅仅是一个特殊值,并不属于任何类型,用instanceof永远返回false。
不能将null赋给基本类型变量,例如int、double、float、boolean。如果将null赋值给包装类object,然后将object赋给各自的基本类型,编译器不会报,但是你将会在运行时期遇到空指针异常。
null可以被转化为任何引用类型,可以调用引用类型中的静态方法,但是不可以调用非静态方法,运行时会报错。

1
2
3
4
5
6
7
8
9
10
11
public class NullTest {
public static void main(String[] args) {
Object o = (Object) null;
//int i = null;
Integer i = (Integer) null;
String s = (String) null;

System.out.println("o: " + o + "i: " + i + "s: " + s); //o: nulli: nulls: null
System.out.println(o instanceof Object); //false
}
}

3. 运算

null==null返回true,被转换为同种类型的null,都返回true,不同类型直接编译报错.
用String转换后的null可以进行字符串运算,这是因为字符串进行连接的时候,编译器对null进行了特别的优化,其实就是例化StringBuilder,在调用append()方法时对null的一个特别处理,当为null时,转化为“null”,最后调用toString()返回一个String对象.
用八大基本类型转换后的null,不可以进行基本类型的运算,否则会出现编译或者运行错误.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class NullTest {
public static void main(String[] args) {
Object o = (Object) null;
Integer i = (Integer) null;
Integer j = (Integer) null;
String s = (String) null;
// System.out.println(Objects.equals(i, j));
// System.out.println(i.equals(s));
// System.out.println(null == null);
// i = i + 1; //运行时空指针

// System.out.println(2 == null);

System.out.println("o: " + o + "i: " + i + "s: " + s);
System.out.println(o instanceof Object);

}
}

4. 集合中的key

集合类 key value super 说明
HashTable 不能为null 不能为null Dictionary 线程安全
ConcurrentHashMap 不能为null 不能为null AbstractMap 线程局部安全
TreeMap 不能为null 可以为null AbstractMap 线程不安全
HashMap 可以为null 可以为null AbstractMap 线程不安全

hash表需要进行hash值运算,key不能为null好理解,如果map中value为null也好理解。
表中不好理解的是HashMap中key可以为null,看下面代码中对null有个特殊处理,索引位置为0。

img

image.png

表中第二个不好理解的点是ConcurrentHashMap中value不能为null的问题。

img

image.png

作者:扎Zn了老Fe
链接:https://www.jianshu.com/p/83fc81baf115
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

OOP:Object Oriented Programming

  • 基于object进行编程

  • 数据&行为

四大特征

  • 抽象,封装(Encapsulation),继承(inheritance),多态(polymorphism)

    • 三大特性就是封装继承多态
抽象
  • abstraction:抽象

    • 不用管内部怎么实现的,内部细节对于用户隐藏的

    • 只暴露必要的/相关的操作&数据

    • 不同层次:函数层次和类层次

    • ![image-20200227091723756](Java OOP&数据结构.assets/image-20200227091723756.png)

封装
  • 不要让别人单独看到,用private

    • 用setxxx和getxxx

    • 优点

      1.类内部可以自由修改
      2.可以对成员变量更准确的控制
      3.隐藏信息,保护数据
      4.降低耦合度

      耦合度:耦合性是编程中的一个判断代码模块构成质量的属性,不影响已有功能,但影响未来拓展,与之对应的是内聚性

      例如:卧室的窗户与墙壁,如果窗子是扣死在墙里的 ,那么修改窗子时,就必须修改墙,这就比较紧密了,但是如果你窗子是按照某种规格的 可以自由拆装的 那么修改的代价就小,耦合度也就低了
      在程序中我们要求 高内聚,低耦合!
      ————————————————
      版权声明:本文为CSDN博主「66Kevin」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
      原文链接:https://blog.csdn.net/weixin_44551646/article/details/93721343

多态
  • 重载:overloading,参数不同,方法名相同,一个类的多态性
  • 重写:overriding,方法名参数都相同,父类子类
    • 不想具有虚函数特性,就加上final
继承
  • 继承:
- >  继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
  >
  >  ### 生活中的继承:
  >
  >  ![img](Java OOP&数据结构.assets/14B0951E-FC75-47A3-B611-4E1883887339.jpg)
  >
  >  兔子和羊属于食草动物类,狮子和豹属于食肉动物类。
  >
  >  食草动物和食肉动物又是属于动物类。
  >
  >  所以继承需要符合的关系是:is-a,父类更通用,子类更具体。
  >
  >  虽然食草动物和食肉动物都是属于动物,但是两者的属性和行为上有差别,所以子类会具有父类的一般特性也会具有自身的特性。
  >
  >  ### 类的继承格式
  >
  >  在 Java 中通过 extends 关键字可以申明一个类是从另外一个类继承而来的,一般形式如下:
  >
  >  ## 类的继承格式
  >
  >  
1
2
3
4
5
class 父类 {
}

class 子类 extends 父类 {
}
> > ### 为什么需要继承 > > 接下来我们通过实例来说明这个需求。 > > 开发动物类,其中动物分别为企鹅以及老鼠,要求如下: > > - 企鹅:属性(姓名,id),方法(吃,睡,自我介绍) > - 老鼠:属性(姓名,id),方法(吃,睡,自我介绍) - 不支持多继承,但能支持多重继承: - ![img](Java OOP&数据结构.assets/types_of_inheritance-1.png) - > 子类拥有父类非 private 的属性、方法。 > > 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。 > > 子类可以用自己的方式实现父类的方法。
Super、this

super 与 this 关键字

super关键字:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。

this关键字:指向自己的引用。

Final

Final

final 关键字声明类可以把类定义为不能继承的,即最终类;或者用于修饰方法,该方法不能被子类重写:

  • 声明类:

    1
    final class 类名 {//类体}
  • 声明方法:

    1
    修饰符(public/private/default/protected) final 返回值类型 方法名(){//方法体}
  • 子类也是父类,可以转换

    构造器

    子类是不继承父类的构造器(构造方法或者构造函数)的,它只是调用(隐式或显式)。如果父类的构造器带有参数,则必须在子类的构造器中显式地通过 super 关键字调用父类的构造器并配以适当的参数列表。

    如果父类构造器没有参数,则在子类的构造器中不需要使用 super 关键字调用父类构造器,系统会自动调用父类的无参构造器。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // SubClass 类继承
    class SubClass extends SuperClass{
    private int n;

    SubClass(){ // 自动调用父类的无参数构造器
    System.out.println("SubClass");
    }

    public SubClass(int n){
    super(300); // 调用父类中带有参数的构造器
    System.out.println("SubClass(int n):"+n);
    this.n = n;
    }
    }
接口
  • Interface关键字用来声明一个接口。下面是接口声明的一个简单例子。

    NameOfInterface.java 文件代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    /* 文件名 : NameOfInterface.java */
    import java.lang.*;
    //引入包

    public interface NameOfInterface
    {
    //任何类型 final, static 字段
    //抽象方法
    }

    接口有以下特性:

    • 接口是隐式抽象的,当声明一个接口的时候,不必使用abstract关键字。
    • 接口中每一个方法也是隐式抽象的,声明时同样不需要abstract关键字。
    • 接口中的方法都是公有的。
    • 可以用public class MammalInt implements Animal{来说明他要有这几个函数

    • 接口的继承

      一个接口能继承另一个接口,和类之间的继承方式比较相似。接口的继承使用extends关键字,子接口继承父接口的方法。

      下面的Sports接口被Hockey和Football接口继承:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      // 文件名: Sports.java
      public interface Sports
      {
      public void setHomeTeam(String name);
      public void setVisitingTeam(String name);
      }

      // 文件名: Football.java
      public interface Football extends Sports
      {
      public void homeTeamScored(int points);
      public void visitingTeamScored(int points);
      public void endOfQuarter(int quarter);
      }

      // 文件名: Hockey.java
      public interface Hockey extends Sports
      {
      public void homeGoalScored();
      public void visitingGoalScored();
      public void endOfPeriod(int period);
      public void overtimePeriod(int ot);
      }

      接口的多继承

      在Java中,类的多继承是不合法,但接口允许多继承。

      在接口的多继承中extends关键字只需要使用一次,在其后跟着继承接口。 如下所示:

      public interface Hockey extends Sports, Event

      以上的程序片段是合法定义的子接口,与类不同的是,接口允许多继承,而 Sports及 Event 可能定义或是继承相同的方法

抽象类
  • 使用abstract class来定义抽象类
    • 不能被实例化,只能继承
      • 抽象方法

        如果你想设计这样一个类,该类包含一个特别的成员方法,该方法的具体实现由它的子类确定,那么你可以在父类中声明该方法为抽象方法。

        Abstract 关键字同样可以用来声明抽象方法,抽象方法只包含一个方法名,而没有方法体。

        抽象方法没有定义,方法名后面直接跟一个分号,而不是花括号。

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        public abstract class Employee
        {
        private String name;
        private String address;
        private int number;

        public abstract double computePay();

        //其余代码
        }

        声明抽象方法会造成以下两个结果:

        • 如果一个类包含抽象方法,那么该类必须是抽象类。
        • 任何子类必须重写父类的抽象方法,或者声明自身为抽象类。

函数

  • 值传递:

    • 对于基本类型 num ,赋值运算符会直接改变变量的值,原来的值被覆盖掉。
      对于引用类型 str,赋值运算符会改变引用中所保存的地址,原来的地址被覆盖掉。但是原来的对象不会被改变(重要)。

    • 因为传递的是引用

    • 作者:Intopass
      链接:https://www.zhihu.com/question/31203609/answer/50992895
      来源:知乎
      著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 三:调用方法时发生了什么?参数传递基本上就是赋值操作

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    第一个例子:基本类型
    void foo(int value) {
    value = 100;
    }
    foo(num); // num 没有被改变

    第二个例子:没有提供改变自身方法的引用类型
    void foo(String text) {
    text = "windows";
    }
    foo(str); // str 也没有被改变

    第三个例子:提供了改变自身方法的引用类型
    StringBuilder sb = new StringBuilder("iphone");
    void foo(StringBuilder builder) {
    builder.append("4");
    }
    foo(sb); // sb 被改变了,变成了"iphone4"。

    第四个例子:提供了改变自身方法的引用类型,但是不使用,而是使用赋值运算符。
    StringBuilder sb = new StringBuilder("iphone");
    void foo(StringBuilder builder) {
    builder = new StringBuilder("ipad");
    }
    foo(sb); // sb 没有被改变,还是 "iphone"。

final:

  • final关键字用法

    修饰类当用final去修饰一个类的时候,表示这个类不能被继承。注意:a. 被final修饰的类,final类中的成员变量可以根据自己的实际需要设计为fianl。b. final类中的成员方法都会被隐式的指定为final方法。说明:在自己设计一个类的时候,要想好这个类将来是否会被继承,如果可以被继承,则该类不能使用fianl修饰,在这里呢,一般来说工具类我们往往都会设计成为一个fianl类。在JDK中,被设计为final类的有String、System等。代码:

    ![img](Java OOP&数据结构.assets/u=2411423419,2520809892&fm=173&app=25&f=JPEG.jpeg)

    \2. 修饰方法

    被final修饰的方法不能被重写。

    注意:

    a. 一个类的private方法会隐式的被指定为final方法。

    b. 如果父类中有final修饰的方法,那么子类不能去重写。

    代码:

    ![img](Java OOP&数据结构.assets/u=876634331,3528497804&fm=173&app=25&f=JPEG.jpeg)

    3. 修饰成员变量

    注意:

    a. 必须要赋初始值,而且是只能初始化一次。

    代码:

    ![img](Java OOP&数据结构.assets/u=433607551,3063868730&fm=173&app=25&f=JPEG.jpeg)

    \4. 修饰成员变量

    注意:

    a. 必须初始化值。

    b. 被fianl修饰的成员变量赋值,有两种方式:1、直接赋值 2、全部在构造方法中赋初值。

    c. 如果修饰的成员变量是基本类型,则表示这个变量的值不能改变。

    d. 如果修饰的成员变量是一个引用类型,则是说这个引用的地址的值不能修改,但是这个引用所指向的对象里面的内容还是可以改变的。

    代码:

    ![img](Java OOP&数据结构.assets/u=682519727,1257936163&fm=173&app=25&f=JPEG.jpeg)

Static:

  • 我们可以一句话来概括:方便在没有创建对象的情况下来进行调用。

  • 2、static关键字修饰类

    java里面static一般用来修饰成员变量或函数。但有一种特殊用法是用static修饰内部类,普通类是不允许声明为静态的,只有内部类才可以。下面看看如何使用。

    ![img](Java OOP&数据结构.assets/8644ebf81a4c510f633a3493cb792028d52aa567.jpeg)

    如果没有用static修饰InterClass,则只能new 一个外部类实例。再通过外部实例创建内部类。

    3、static关键字修饰方法

    修饰方法的时候,其实跟类一样,可以直接通过类名来进行调用:

    ![img](Java OOP&数据结构.assets/4afbfbedab64034f7b86b4dc05e37c340b551d41.jpeg)

    4、static关键字修饰变量

    被static修饰的成员变量叫做静态变量,也叫做类变量,说明这个变量是属于这个类的,而不是属于是对象,没有被static修饰的成员变量叫做实例变量,说明这个变量是属于某个具体的对象的。

    我们同样可以使用上面的方式进行调用变量:

    ![img](Java OOP&数据结构.assets/d8f9d72a6059252d0a2aeb289fbb063e5bb5b987.jpeg)

    5、static关键字修饰代码块

    静态代码块在类第一次被载入时执行,在这里主要是想验证一下,类初始化的顺序。

    父类静态变量

    父类静态代码块

    子类静态变量

    子类静态代码块

    父类普通变量

    父类普通代码块

    父类构造函数

    子类普通变量

    子类普通代码块

    子类构造函数

    代码验证一下:

    首先我们定义一个父类

    ![img](Java OOP&数据结构.assets/faf2b2119313b07ed299fae9a6f7942696dd8cd4.jpeg)

    然后定义一个子类

    ![img](Java OOP&数据结构.assets/37d3d539b6003af36f87b1b99f0ac3591138b6cb.jpeg)

    看个结果

    ![img](Java OOP&数据结构.assets/d009b3de9c82d158c5bd9c3e2a2a1cddbc3e4223.jpeg)

    二、深入分析static关键字

    上面我们只是描述了一下static关键字的基本使用场景,下面主要解析一下static关键字的深层原理。要理解static为什么会有上面的特性,首先我们还需要从jvm内存说起。我们先给出一张java的内存结构图,然后通过案例描述一下static修饰的变量存放在哪?

    ![img](Java OOP&数据结构.assets/024f78f0f736afc33409f1471839eec1b74512b4.jpeg)

    从上图我们可以发现,静态变量存放在方法区中,并且是被所有线程所共享的。这里要说一下java堆,java堆存放的就是我们创建的一个个实例变量。

    堆区:

    1、存储的全部是对象,每个对象都包含一个与之对应的class的信息。(class的目的是得到操作指令)

    2、jvm只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身

    栈区:

    1.每个线程包含一个栈区,栈中只保存基础数据类型的对象和自定义对象的引用(不是对象),对象都存放在堆区中

    2、每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问。

    3、栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。、

    方法区:

    1、又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的class和static变量。

    2、方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。

    下面通过一个案例说明一下,从内存的角度来看,static关键字为什么会有这样的特性。

    首先我们定义一个类

    ![img](Java OOP&数据结构.assets/359b033b5bb5c9ea023ba6fe7e19b3053bf3b38c.jpeg)

    接下来我们从内存的角度出发,看看

    ![img](Java OOP&数据结构.assets/f3d3572c11dfa9ec028d9199c8f0f206908fc147.jpeg)

    从上面可以看到,我们的方法在调用的时候,是从方法区调用的,但是堆内存不一样,堆内存中的成员变量lastname是随着对象的产生而产生。随着对象的消失而消失。静态变量是所有线程共享的,所以不会消失。这也就能解释上面static关键字的真正原因。

    下面对static关键字进行一个小结:

    (1)特点:

    1、static是一个修饰符,用于修饰成员。(成员变量,成员函数)static修饰的成员变量 称之为静态变量或类变量。

    2、static修饰的成员被所有的对象共享。

    3、static优先于对象存在,因为static的成员随着类的加载就已经存在。

    4、static修饰的成员多了一种调用方式,可以直接被类名所调用,(类名.静态成员)。

    5、static修饰的数据是共享数据,对象中的存储的是特有的数据。

    (2)成员变量和静态变量的区别:

    1、生命周期的不同:

    成员变量随着对象的创建而存在随着对象的回收而释放。

    静态变量随着类的加载而存在随着类的消失而消失。

    2、调用方式不同:

    成员变量只能被对象调用。

    静态变量可以被对象调用,也可以用类名调用。(推荐用类名调用)

    3、别名不同:

    成员变量也称为实例变量。

    静态变量称为类变量。

    4、数据存储位置不同:

    成员变量数据存储在堆内存的对象中,所以也叫对象的特有数据。

    静态变量数据存储在方法区(共享数据区)的静态区,所以也叫对象的共享数据。

    (3)静态使用时需要注意的事项:

    1、静态方法只能访问静态成员。(非静态既可以访问静态,又可以访问非静态)

    2、静态方法中不可以使用this或者super关键字。

    3、主函数是静态的

    好了,static关键字就介绍道这里,谢谢您的支持,如有问题,还请批评指正

  • static代码块也叫静态代码块,是在类中独立于类成员的static语句块,可以有多个,位置可以随便放,它不在任何的方法体内,JVM加载类时会执行这些静态的代码块,如果static代码块有多个,JVM将按照它们在类中出现的先后顺序依次执行它们,每个代码块只会被执行一次。例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    public class Test5 {
    private static int a;
    private int b;

    static{
    Test5.a=3;
    System.out.println(a);
    Test5 t=new Test5();
    t.f();
    t.b=1000;
    System.out.println(t.b);
    }
    static{
    Test5.a=4;
    System.out.println(a);
    }
    public static void main(String[] args) {
    // TODO 自动生成方法存根
    }
    static{
    Test5.a=5;
    System.out.println(a);
    }
    public void f(){
    System.out.println("hhahhahah");
    }
    }

      运行结果:

    1
    2
    3
    4
    5
    3
    hhahhahah
    1000
    4
    5

      利用静态代码块可以对一些static变量进行赋值,最后再看一眼这些例子,都一个static的main方法,这样JVM在运行main方法的时候可以直接调用而不用创建实例。 

      4、static和final一块用表示什么
      static final用来修饰成员变量和成员方法,可简单理解为“全局常量”!
      对于变量,表示一旦给值就不可修改,并且通过类名可以访问。
      对于方法,表示不可覆盖,并且可以通过类名直接访问。

this:

  • 指代自己变量的,防止被覆盖
  • 可以调用对应的构造函数