Java基础-入门
大学期间我已经学过 C、C++、Python、Java 等语言,其中 Java 前后学了有一年半(基本围绕着各种CRM管理系统进行CRUD开发),Python 学了大概半年多(基本围绕着Tensorflow做强化学习+深度学习,有论文产出,中间还看过爬虫),C++将近一年(保研结束后整体比较松散,基本围绕着C++11学习语法、看一些C项目等),现在明确了以后走后端开发决定重新捡起 Java,真正透彻的掌握 Java 相关知识。
参考文章:菜鸟教程-Java特性
1.主要特性
- Java 语言是简单的
Java 语言的语法与 C 语言和 C++ 语言很接近,使得大多数程序员很容易学习和使用。另一方面,Java 丢弃了 C++ 中很少使用的、很难理解的、令人迷惑的那些特性,如操作符重载、多继承、自动的强制类型转换。特别地,Java 语言不使用指针而是引用,并提供了自动分配和回收内存空间,使得程序员不必为内存管理而担忧。
Java 和 C++ 作为后端开发中的两种主流语言(当然还有Go),其使用形式非常接近,不过写过 C++ 的便知道 C++ 明显更加细致和啰嗦,而且 C++ 因为严谨和复杂一些特性比较抽象,例如 C++ 中通过运算符重载能够完成 Student1 + Student2 这种类相加操作,Java则完全不允许 + 运算符的这种行为;C++ 中子类还能够同时继承多个父类,当父类中有名称一样的属性时便发生了“多重继承二义性”现象,此时子类访问同名属性需要作用域限定符 “::”,并且 C++ 只针对父类的虚函数进行动态绑定(虚函数表原理),Java 则完全不需要担心这些东西。最重要的一点是,Java 不使用指针而是引用,消除了 C++ 程序员最为头疼的内存分配与回收的问题(垃圾回收原理)。
- Java 语言是面向对象的
Java 语言提供类、接口和继承等面向对象的特性,为了简单起见,只支持类的单继承,但支持接口的多继承,并支持类与接口之间的实现机制(关键字为 implements
)。Java 语言全面支持动态绑定
,而 C++ 语言只对虚函数使用动态绑定。总之,Java 语言是一个纯粹的面向对象程序设计语言。
C++ 也是面向对象的编程语言,同时支持面向模板的泛型编程,还保留了 C 语言的低级特性。Java 过于纯粹了哦!
- Java 语言是分布式的
Java 语言支持 Internet 应用的开发,在基本的 Java 应用编程接口中有一个网络应用编程接口(java net),它提供了用于网络应用编程的类库,包括 URL、URLConnection、Socket、ServerSocket 等。Java 的 RMI(远程方法激活)机制也是开发分布式应用的重要手段。
Java 本身并不是”分布式的”,但 Java 在设计和实现上考虑了分布式计算的需求,因此可以很容易地用于开发分布式系统。如网络编程(Java 提供了丰富的网络编程 API)、RMI(远程方法调用)、JNDI(Java 命名和目录接口,JNDI 提供了访问命名和目录服务的 API,使得分布式系统可以轻松查找和访问不同的资源)、序列化(Java 的序列化机制允许将 Java 对象转换为字节流,并在不同计算机之间传输,实现对象的跨网络传输)等。
- Java 语言是健壮的
Java 的强类型机制、异常处理、垃圾的自动收集等是 Java 程序健壮性的重要保证。对指针的丢弃是 Java 的明智选择。Java 的安全检查机制使得 Java 更具健壮性。
Java 是静态类型语言,编译器在编译时会进行类型检查,Java 提供了异常处理机制,允许开发者在程序出现异常时进行适当处理,防止程序崩溃或安全漏洞,同时 Java 的垃圾回收机制自动处理不再使用的对象,防止内存泄漏,减少了许多与动态内存管理相关的问题。
- Java 语言是安全的
Java 通常被用在网络环境中,为此,Java 提供了一个安全机制以防恶意代码的攻击。除了 Java 语言具有的许多安全特性以外,Java 对通过网络下载的类具有一个安全防范机制(类 ClassLoader
),如分配不同的名字空间以防替代本地的同名类、字节代码检查,并提供安全管理机制(类 SecurityManager
)让 Java 应用设置安全哨兵。
Java 的安全性体现在很多方面。例如强类型检查、数组边界检查、无指针、访问控制、类加载机制、安全管理机制等。有趣的是,Java 中没有全局变量的概念(只有实例变量、静态变量、局部变量),所有的变量都必须定义在类的作用域内,这样才能保证变量的访问权限和生命周期。
- 局部变量存储在栈内存中,具有较短的生命周期,当方法执行完毕或离开作用域时,局部变量被销毁。
- 成员变量存储在堆内存中,与对象的生命周期相同,当对象被销毁时,成员变量的内存空间也会被回收。
- 静态变量存储在方法区中,与类的生命周期相同,当类被卸载时,静态变量的内存空间将被释放。
- Java 语言是体系结构中立的
Java 程序(后缀为 .java 的文件)在 Java 平台上被编译为体系结构中立的字节码格式(后缀为 .class 的文件),然后可以在实现这个 Java 平台的任何系统中运行。这种途径适合于异构的网络环境和软件的分发。
Java 虚拟机是 Java 语言的核心执行环境,它充当了 Java 程序与底层操作系统之间的中间层。不同平台上的 JVM 实现可以解释执行相同的字节码,从而实现了跨平台的特性。然而,需要注意的是,Java 的平台中立性主要体现在 Java 源代码和字节码层面。一旦涉及到与底层操作系统直接交互的部分,如文件操作、网络套接字等,就可能涉及到平台相关性,需要特别注意处理平台差异。因此,在编写跨平台的 Java 程序时,需要避免直接依赖于底层系统特定的功能。
- Java 语言是可移植的
这种可移植性来源于体系结构中立性
,另外,Java 还严格规定了各个基本数据类型的长度。Java 系统本身也具有很强的可移植性,Java 编译器是用 Java 实现的,Java 的运行环境是用 ANSI C 实现的。
Java 的跨平台特性使得开发者可以在一台平台上编写 Java 程序,然后将生成的字节码文件拷贝到其他平台的 JVM 上执行,而不需要重新编译。
- Java 语言是解释型的
如前所述,Java 程序在 Java 平台上被编译为字节码格式,然后可以在实现这个 Java 平台的任何系统中运行。在运行时,Java 平台中的 字节码解释器对这些字节码进行解释执行,执行过程中需要的类在联接阶段被载入到运行环境中。
准确的说,Java 语言是一种混合型语言,Java 程序的执行过程涉及解释和编译两个阶段:
- 编译阶段: Java 源代码首先经过编译器编译成中间代码,称为字节码(Bytecode),它是一种与平台无关的中间形式。
- 解释阶段: 在执行阶段,Java 虚拟机(JVM)负责将字节码解释成特定平台的机器码,或者通过即时编译(JIT Compilation)技术将部分字节码编译成本地机器码,以提高执行效率。
- Java 是高性能的
与那些解释型的高级脚本语言相比,Java 的确是高性能的。事实上,Java 的运行速度随着 JIT(Just-In-Time)编译器技术的发展越来越接近于 C++。
Java是一种相对高性能的编程语言,但性能取决于具体的使用场景和优化措施。
- 即时编译(JIT Compilation): Java 虚拟机(JVM)使用即时编译器(JIT Compiler)将字节码转换成本地机器码,从而提高程序的执行速度。JIT 编译可以根据代码的执行情况进行优化,对热点代码进行特殊处理,从而进一步提高性能。
- 垃圾回收(Garbage Collection): Java 的内存管理是由垃圾回收机制来处理的。垃圾回收可以自动回收不再使用的内存,避免了内存泄漏和悬挂指针等问题。但垃圾回收也可能导致一定的性能损耗,特别是在进行大规模内存回收时。因此,在高性能应用中,需要适当地调整垃圾回收策略和选择合适的垃圾回收器。
- 多线程支持: Java 具有强大的多线程支持,可以充分利用多核处理器的性能。但同时,多线程编程也带来了线程安全和同步的挑战,如果不正确地处理多线程问题,可能会导致性能下降或者出现并发问题。
- 底层交互: Java 提供了本地方法接口(JNI),可以与底层系统进行交互。在一些性能敏感的场景,可以使用 JNI 来调用 C 或C++ 编写的本地库,以获得更高的性能。
- 平台中立性: Java 的跨平台特性允许同一份 Java 代码在不同平台上运行,但有时为了实现跨平台性,可能需要在某些特定平台上做出性能上的妥协。
2.Java关键字
Java关键字类别 | Java关键字 | 关键字含义 |
---|---|---|
访问控制 | private | 一种访问控制方式:私有模式,可以应用于类、方法或字段(在类中声明的变量)的访问控制修饰符 |
访问控制 | protected | 一种访问控制方式:保护模式,可以应用于类、方法或字段(在类中声明的变量)的访问控制修饰符 |
访问控制 | public | 一种访问控制方式:共用模式,可以应用于类、方法或字段(在类中声明的变量)的访问控制修饰符。 |
类、方法和变量修饰符 | abstract | 表明类或者成员方法具有抽象属性,用于修改类或方法 |
类、方法和变量修饰符 | class | 声明一个类,用来声明新的Java类 |
类、方法和变量修饰符 | extends | 表明一个类型是另一个类型的子类型。对于类,可以是另一个类或者抽象类;对于接口,可以是另一个接口 |
类、方法和变量修饰符 | final | 用来说明最终属性,表明一个类不能派生出子类,或者成员方法不能被覆盖,或者成员域的值不能被改变,用来定义常量 |
类、方法和变量修饰符 | implements | 表明一个类实现了给定的接口 |
类、方法和变量修饰符 | interface | 接口 |
类、方法和变量修饰符 | native | 用来声明一个方法是由与计算机相关的语言(如C/C++/FORTRAN语言)实现的 |
类、方法和变量修饰符 | new | 用来创建新实例对象 |
类、方法和变量修饰符 | static | 表明具有静态属性 |
类、方法和变量修饰符 | strictfp | 用来声明FP_strict(单精度或双精度浮点数)表达式遵循IEEE 754算术规范 |
类、方法和变量修饰符 | synchronized | 表明一段代码需要同步执行 |
类、方法和变量修饰符 | transient | 声明不用序列化的成员域 |
类、方法和变量修饰符 | volatile | 表明两个或者多个变量必须同步地发生变化 |
程序控制 | break | 提前跳出一个块 |
程序控制 | continue | 回到一个块的开始处 |
程序控制 | return | 从成员方法中返回数据 |
程序控制 | do | 用在do-while循环结构中 |
程序控制 | while | 用在循环结构中 |
程序控制 | if | 条件语句的引导词 |
程序控制 | else | 用在条件语句中,表明当条件不成立时的分支 |
程序控制 | for | 一种循环结构的引导词 |
程序控制 | instanceof | 用来测试一个对象是否是指定类型的实例对象 |
程序控制 | switch | 分支语句结构的引导词 |
程序控制 | case | 用在switch语句之中,表示其中的一个分支 |
程序控制 | default | 默认,例如:用在switch语句中,表明一个默认的分支。Java8 中也作用于声明接口函数的默认实现 |
错误处理 | try | 尝试一个可能抛出异常的程序块 |
错误处理 | catch | 用在异常处理中,用来捕捉异常 |
错误处理 | throw | 抛出一个异常 |
错误处理 | throws | 声明在当前定义的成员方法中所有需要抛出的异常 |
包相关 | import | 表明要访问指定的类或包 |
包相关 | package | 包 |
基本类型 | boolean | 基本数据类型之一,声明布尔类型的关键字 |
基本类型 | byte | 基本数据类型之一,字节类型 |
基本类型 | char | 基本数据类型之一,字符类型 |
基本类型 | double | 基本数据类型之一,双精度浮点数类型 |
基本类型 | float | 基本数据类型之一,单精度浮点数类型 |
基本类型 | int | 基本数据类型之一,整数类型 |
基本类型 | long | 基本数据类型之一,长整数类型 |
基本类型 | short | 基本数据类型之一,短整数类型 |
字面量 | null |
空,表示无值,不能将null赋给原始类型(byte、short、int、long、char、float、double、boolean)变量 |
字面量 | true |
真,boolean变量的两个合法值中的一个 |
字面量 | false |
假,boolean变量的两个合法值之一 |
变量引用 | super | 表明当前对象的父类型的引用或者父类型的构造方法 |
变量引用 | this | 指向当前实例对象的引用,用于引用当前实例 |
变量引用 | void | 声明当前成员方法没有返回值,void可以用作方法的返回类型,以指示该方法不返回值 |
保留字 | ==goto== | 保留关键字,没有具体含义 |
保留字 | ==const== | 保留关键字,没有具体含义,是一个类型修饰符,使用const声明的对象不能更新 |
Java 的 null 不是关键字,类似于 true 和 false,它是一个字面常量,不允许作为标识符使用。
3.源文件声明规则
当在一个源文件中定义多个类,并且还有 import
语句和 package
语句时,要特别注意这些规则。
- 一个源文件中只能有一个 public 类,但可以有多个非 public 类,源文件的名称应该和 public 类的类名保持一致
- 如果一个类定义在某个包中,那么 package 语句应该在源文件的首行,如果源文件包含 import 语句,那么应该放在 package 语句和类定义之间
- import 语句和 package 语句对源文件中定义的所有类都有效
4.基本数据类型
1 | public class PrimitiveTypeTest { |
Java 语言对布尔类型的存储并没有做规定,因为理论上存储布尔类型只需要1 bit,但是通常 JVM 内部会把boolean
表示为4字节整数。
一般谈到数据类型就逃不过类型转换这个话题,Java 同样如此,表达式中不同类型的数据需要先转换成同一类型(往较大类型转型),再进行运算。当范围大的类型转换成范围小的类型时需要注意:可能发生
数值溢出
和精度损失
。注意:可以将浮点型强制转为整型,但超出范围后将始终返回整型的最大值。
额外补充一个细节:位移运算符有三种:<<
,>>
,>>>
,其中>>>
不管符号位,右移时高位总是补0
5.变量类型
Java 中成员变量
和静态变量
有默认的初始化值,而局部变量
必须初始化才能使用,这是编译器的规定:
1 | public class VariableInitialization { |
方法参数变量的值传递方式有两种:值传递和引用传递。
- 值传递:在方法调用时,传递的是实际参数的值的副本。当参数变量被赋予新的值时,只会修改副本的值,不会影响原始值。Java 中的基本数据类型都采用值传递方式传递参数变量的值。
- 引用传递:在方法调用时,传递的是实际参数的引用(即内存地址)。当参数变量被赋予新的值时,会修改原始值的内容。Java 中的对象类型采用引用传递方式传递参数变量的值。
实际上,Java 中的引用类型如数组对象、类对象也可以理解为值传递,只不过这个值是引用类型对象的内存地址值。
Java 中的引用类似于 C++ 中的指针,Java 引用"指向"堆中分配的类对象
,传递引用类似于传递指针,参数变量与原变量引用同一个堆中的类对象,如果方法体内修改了引用的指向,则后续操作便不再影响原变量的内容!
1 | public class PassByReference { |
Java 中没有指针的概念,但引用类似于指针,直观地看,针对引用类型的参数变量的修改(内部属性修改)会直接影响到原变量,这跟指针的工作方式不谋而合。
6.Java修饰符
访问修饰符 | 当前类 | 同一包内 | 子孙类(同一包) | 子孙类(不同包) | 其他包 |
---|---|---|---|---|---|
public |
Y | Y | Y | Y | Y |
protected |
Y | Y | Y | Y/N | N |
default |
Y | Y | Y | N | N |
private |
Y | N | N | N | N |
protected 访问控制修饰符的作用范围比较难以理解,具体如下:
- 基类的 protected 成员是包内可见的,并且对子类可见;
- 若子类与基类不在同一包中,那么在子类中,子类实例可以访问其从基类继承而来的protected方法,而不能访问基类实例的protected方法。
总结来说:与 protected 修饰的成员的基类所在同一个包的其他类
可以随意访问(同一个包下该基类的子类的成员方法中可以直接使用继承的 protected 成员,也能通过基类实例访问) ;如果该基类的子类与基类不在同一个包下,则子类只能在成员方法中使用继承的 protected 成员。
非访问修饰符也有很多,它们都很重要,以后会单独说。