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。image.png
表中第二个不好理解的点是ConcurrentHashMap中value不能为null的问题。
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
10public 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
27public 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
53
hhahhahah
1000
4
5利用静态代码块可以对一些static变量进行赋值,最后再看一眼这些例子,都一个static的main方法,这样JVM在运行main方法的时候可以直接调用而不用创建实例。
4、static和final一块用表示什么
static final用来修饰成员变量和成员方法,可简单理解为“全局常量”!
对于变量,表示一旦给值就不可修改,并且通过类名可以访问。
对于方法,表示不可覆盖,并且可以通过类名直接访问。
this:
- 指代自己变量的,防止被覆盖
- 可以调用对应的构造函数