Java面向对象编程基础

Posted 无影飞絮剑

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java面向对象编程基础相关的知识,希望对你有一定的参考价值。

1、

在OOP的术语中,我们把Person称为超类(super class),父类(parent class),基类(base class),把Student称为子类(subclass),扩展类(extended class)。

Java只允许一个class继承自一个类,因此,一个类有且仅有一个父类。

继承有个特点,就是子类无法访问父类的private字段或者private方法。用protected修饰的字段可以被子类访问。protected关键字可以把字段和方法的访问权限控制在继承树内部,一个protected字段和方法可以被其子类,以及子类的子类所访问。

在Java中,任何class的构造方法,第一行语句必须是调用父类的构造方法。如果没有明确地调用父类的构造方法,编译器会帮我们自动加一句super();

如果父类没有默认的构造方法,子类就必须显式调用super()并给出参数以便让编译器定位到父类的一个合适的构造方法。

即子类不会继承任何父类的构造方法。子类默认的构造方法是编译器自动生成的,不是继承的。

instanceof实际上判断一个变量所指向的实例是否是指定类型,或者这个类型的子类。如果一个引用变量为null,那么对任何instanceof的判断都为false

继承是is关系,组合是has关系

  • 继承是面向对象编程的一种强大的代码复用方式;

  • Java只允许单继承,所有类最终的根类是Object

  • protected允许子类访问父类的字段和方法;

  • 子类的构造方法可以通过super()调用父类的构造方法;

  • 可以安全地向上转型为更抽象的类型;

  • 可以强制向下转型,最好借助instanceof判断;

  • 子类和父类的关系是is,has关系不能用继承。

2、

在继承关系中,子类如果定义了一个与父类方法签名完全相同的方法,被称为覆写(Override)。

Java的实例方法调用是基于运行时的实际类型的动态调用,而非变量的声明类型。

这个非常重要的特性在面向对象编程中称之为多态。它的英文拼写非常复杂:Polymorphic。

多态是指,针对某个类型的方法调用,其真正执行的方法取决于运行时期实际类型的方法。

多态的特性就是,运行期才能动态决定调用的子类方法。

继承可以允许子类覆写父类的方法。如果一个父类不允许子类对它的某个方法进行覆写,可以把该方法标记为final。用final修饰的方法不能被Override

对于一个类的实例字段,同样可以用final修饰。用final修饰的字段在初始化后不能被修改。

  • 子类可以覆写父类的方法(Override),覆写在子类中改变了父类方法的行为;

  • Java的方法调用总是作用于运行期对象的实际类型,这种行为称为多态;

  • final修饰符有多种作用:

    • final修饰的方法可以阻止被覆写;

    • final修饰的class可以阻止被继承;

    • final修饰的field必须在创建对象时初始化,随后不可修改。

3、

如果一个class定义了方法,但没有具体执行代码,这个方法就是抽象方法,抽象方法用abstract修饰。

因为无法执行抽象方法,因此这个类也必须申明为抽象类(abstract class)。

无法实例化的抽象类有什么用?因为抽象类本身被设计成只能用于被继承,因此,抽象类可以强迫子类实现其定义的抽象方法,否则编译会报错。因此,抽象方法实际上相当于定义了“规范”。

尽量引用高层类型,避免引用实际子类型的方式,称之为面向抽象编程。

面向抽象编程的本质就是:

  • 上层代码只定义规范(例如:abstract class Person);

  • 不需要子类就可以实现业务逻辑(正常编译);

  • 具体的业务逻辑由不同的子类实现,调用者并不关心

 

  • 通过abstract定义的方法是抽象方法,它只有定义,没有实现。抽象方法定义了子类必须实现的接口规范;

  • 定义了抽象方法的class必须被定义为抽象类,从抽象类继承的子类必须实现抽象方法;

  • 如果不实现抽象方法,则该子类仍是一个抽象类;

  • 面向抽象编程使得调用者只关心抽象方法的定义,不关心子类的具体实现。

4、

如果一个抽象类没有字段,所有方法全部都是抽象方法,就可以把该抽象类改写为接口:interface

所谓interface,就是比抽象类还要抽象的纯抽象接口,因为它连字段都不能有。因为接口定义的所有方法默认都是public abstract的,所以这两个修饰符不需要写出来(写不写效果都一样)。

当一个具体的class去实现一个interface时,需要使用implements关键字。

在Java中,一个类只能继承自另一个类,不能从多个类继承。但是,一个类可以实现多个interface

Java的接口特指interface的定义,表示一个接口类型和一组方法签名,而编程接口泛指接口规范,如方法签名,数据格式,网络协议等。

抽象类和接口的对比如下:

 abstract classinterface
继承 只能extends一个class 可以implements多个interface
字段 可以定义实例字段 不能定义实例字段
抽象方法 可以定义抽象方法 可以定义抽象方法
非抽象方法 可以定义非抽象方法 可以定义default方法

 

 

 

 

在使用的时候,实例化的对象永远只能是某个具体的子类,但总是通过接口去引用它,因为接口比抽象类更抽象(Iterable it =(Collection coll =(new ArrayList()

实现类可以不必覆写default方法。default方法的目的是,当我们需要给接口新增一个方法时,会涉及到修改全部子类。如果新增的是default方法,那么子类就不必全部修改,只需要在需要覆写的地方去覆写新增方法(调用时有实现)。

default方法和抽象类的普通方法是有所不同的。因为interface没有字段,default方法无法访问字段,而抽象类的普通方法可以访问实例字段

Java的接口(interface)定义了纯抽象规范,一个类可以实现多个接口;

接口也是数据类型,适用于向上转型和向下转型;

接口的所有方法都是抽象方法,接口不能定义实例字段;

接口可以定义default方法(JDK>=1.8)。

5、

Java定义了一种名字空间,称之为包:package

在Java虚拟机执行的时候,JVM只看完整类名,因此,只要包名不同,类就不同。

包可以是多层结构,用.隔开。例如:java.util。包没有父子关系。java.util和java.util.zip是不同的包,两者没有任何继承关系。

位于同一个包的类,可以访问包作用域的字段和方法。不用publicprotectedprivate修饰的字段和方法就是包作用域。

import.*,表示把这个包下面的所有class都导入进来(但不包括子包的class

import static的语法,它可以导入可以导入一个类的静态字段和静态方法

Java编译器最终编译出的.class文件只使用完整类名,因此,在代码中,当编译器遇到一个class名称时:

    • 如果是完整类名,就直接根据完整类名查找这个class

    • 如果是简单类名,按下面的顺序依次查找:

      • 查找当前package是否存在这个class

      • 查找import的包是否包含这个class

      • 查找java.lang包是否包含这个class

如果按照上面的规则还无法确定类名,则编译报错。

编写class的时候,编译器会自动帮我们做两个import动作:

    • 默认自动import当前package的其他class

    • 默认自动import java.lang.*

Java内建的package机制是为了避免class命名冲突;

JDK的核心类使用java.lang包,编译器会自动导入;

JDK的其它常用类定义在java.util.*java.math.*java.text.*,……;

包名推荐使用倒置的域名,例如org.apache

 

Java内建的访问权限包括publicprotectedprivatepackage权限;

Java在方法内部定义的变量是局部变量,局部变量的作用域从变量声明开始,到一个块结束;

final修饰符不是访问权限,它可以修饰classfieldmethod

一个.java文件只能包含一个public类,但可以包含多个非public类。

6、

classpath是JVM用到的一个环境变量,它用来指示JVM如何搜索classclasspath就是一组目录的集合,它设置的搜索路径与操作系统相关。

在Windows系统上,用;分隔,带空格的目录用""括起来;在Linux系统上,用:分隔。

classpath的设定方法有两种:

在系统环境变量中设置classpath环境变量,不推荐;

在启动JVM时设置classpath变量,推荐。

JVM通过环境变量classpath决定搜索class的路径和顺序;

不推荐设置系统环境变量classpath,始终建议通过-cp命令传入;

jar包相当于目录,可以包含很多.class文件,方便下载和使用;

MANIFEST.MF文件可以提供jar包的信息,如Main-Class,这样可以直接运行jar包。

7、

.class文件是JVM看到的最小可执行文件,jar文件就是class文件的容器。.jmod模块,jre是Java运行时环境。

如果外部代码想要访问我们的模块中的类,我们必须将其导出(exports 类)。

JVM自带的标准库rt.jar,从Java 9开始,原有的Java标准库已经由一个单一巨大的rt.jar分拆成了几十个模块,这些模块以.jmod扩展名标识。

Java 9引入的模块目的是为了管理依赖;

使用模块可以按需打包JRE;

使用模块对类的访问权限有了进一步限制。

8、

两个字符串比较,必须总是使用equals()方法。

要忽略大小写比较,使用equalsIgnoreCase()方法。

contains()方法的参数是CharSequence而不是String,因为CharSequenceString的父类。

trim()方法可以移除字符串首尾空白字符。空白字符包括空格,

strip()方法也可以移除字符串首尾空白字符和中文的空格字符u3000

isEmpty()isBlank()来判断字符串是否为空和空白字符串

join()用指定的字符串连接字符串数组

静态方法valueOf()要把任意基本类型或引用类型转换为字符串

Integer.parseInt、Boolean.parseBoolean、toCharArray()
Integer有个getInteger(String)方法,它不是将字符串转换为int,而是把该字符串对应的系统变量转换为Integer

new String(char[])创建新的String实例时,它并不会直接引用传入的char[]数组,而是会复制一份,所以,修改外部的char[]数组不会影响String实例内部的char[]数组,因为这是两个不同的数组。

String的不变性设计可以看出,如果传入的对象有可能改变,我们需要复制而不是直接引用。

Java的Stringchar在内存中总是以Unicode编码表示。

    • Java字符串String是不可变对象;

    • 字符串操作不改变原字符串内容,而是返回新字符串;

    • 常用的字符串操作:提取子串、查找、替换、大小写转换等;

    • Java使用Unicode编码表示Stringchar

    • 转换编码就是将Stringbyte[]转换,需要指定编码;

    • 转换为byte[]时,始终优先考虑UTF-8编码

StringBuilder是可变对象,用来高效拼接字符串;

StringBuilder可以支持链式操作,实现链式操作的关键是返回实例本身;

StringBufferStringBuilder的线程安全版本,现在很少使用。

用指定分隔符拼接字符串数组时,使用StringJoiner或者String.join()更方便;

StringJoiner拼接字符串时,还可以额外附加一个“开头”和“结尾”。

9、

Java核心库提供的包装类型可以把基本类型包装为class,包装类型就是基本类型可以为null

自动装箱和自动拆箱都是在编译期完成的(JDK>=1.5);

装箱和拆箱会影响执行效率,且拆箱时可能发生NullPointerException

包装类型的比较必须使用equals()

整数和浮点数的包装类型都继承自Number

包装类型提供了大量实用方法。

10、

在Java中,有很多class的定义都符合这样的规范,称为JavaBean类:

    • 若干private实例字段;
    • 通过public方法来读写实例字段。

public Type getXyz()

public void setXyz(Type value)

JavaBean是一种符合命名规范的class,它通过gettersetter来定义属性;

属性是一种通用的叫法,并非Java语法规定;

可以利用IDE快速生成gettersetter

使用Introspector.getBeanInfo()可以获取属性列表。

11、

enum类型的每个常量在JVM中只有一个唯一实例,所以可以直接用==比较

name()返回常量名 判断枚举常量的名字,要始终使用name()方法,绝不能调用toString()

ordinal() 返回定义的常量的顺序,从0开始计数;改变枚举常量定义的顺序就会导致ordinal()返回值发生变化

枚举类的字段也可以是非final类型,即可以在运行期修改,但是不推荐这样做!

MON(1, "星期一"),给枚举添加常量字段和重写toString()方法

 

Java使用enum定义枚举类型,它被编译器编译为final class Xxx extends Enum { … }

通过name()获取常量定义的字符串,注意不要使用toString()

通过ordinal()返回常量定义的顺序(无实质意义);

可以为enum编写构造方法、字段和方法

enum的构造方法要声明为private,字段强烈建议声明为final

enum适合用在switch语句中。

12、

在Java中,由CPU原生提供的整型最大范围是64位long型整数。使用long型整数可以直接通过CPU指令进行计算,速度非常快。

java.math.BigInteger就是用来表示任意大小的整数。BigInteger内部用一个int[]数组来模拟一个非常大的整数(用软件来模拟一个超过long的大整数),缺点是速度比较慢。

BigInteger用于表示任意大小的整数;

BigInteger是不变类,并且继承自Number

BigInteger转换成基本类型时可使用longValueExact()等方法保证结果准确。

超过了基本类型的范围,转换时将丢失高位信息,结果不准确;使用ValueExact()等方法超过抛出ArithmeticException异常Infinity

 

BigDecimal用于表示精确的小数,常用于财务计算;

比较BigDecimal的值是否相等,必须使用compareTo()而不能使用equals()


Math会尽量针对平台优化计算速度

StrictMath解决由于浮点数计算存在误差,不同的平台(例如x86和ARM)计算的结果可能不一致(指误差不同)

Random实例时,如果不给定种子,就使用系统当前时间戳作为种子,因此每次运行时,种子不同,得到的伪随机数序列就不同

SecureRandom的安全性是通过操作系统提供的安全的随机种子来生成随机数。这个种子是通过CPU的热噪声、读写磁盘的字节、网络流量等各种随机事件产生的“熵”。

 

 

 

 

 

以上是关于Java面向对象编程基础的主要内容,如果未能解决你的问题,请参考以下文章

Java基础教程:面向对象编程

JAVA学习:Java面向对象编程基础

Java基础教程:面向对象编程[2]

Java编程基础-面向对象(上)

Java编程基础-面向对象(中)

Java基础编程篇(4.面向对象上)