Java-第一章认识和理解Java

第一章认识Java

1 java历史

Sun Microsystems公司于1995年5月推出的Java程序设计语言和Java开发平台。Java是一种面向对

象的编程语言,它的前身是詹姆斯·高斯林(James Gosling,人称java之父)等人于1990年代初开

发的一种编程语言,最初被命名为Oak。

2 平台

1998年12月4日,Sun公司在发布的JDK1.2版本中,将Java技术体系拆分为3个方向(平台):

1. J2SE (Java 2 Platform, Standard Edition),面向桌面应用开发

2. J2EE (Java 2 Platform, Enterprise Edition),面向企业级开发

3. J2ME (Java 2 Platform, Micro Edition),面向手机等移动终端开发

注意,JDK中会自带一个JRE的,也可以专门独立的只安装JRE,但开发人员一定是安装JDK

3 JDK

3.1 相关名词

SDK (software development kit),软件开发包,主要包含函数库或者工具等

JDK (java development kit),java程序开发工具包,面向java程序的开发者

JRE (java runtime enviroment),java程序运行环境,面向java程序的使用者

API (application program interface),应用程序编程接口

API Documentation,API说明文档,描述API中的类、方法等使用的方式

注意,JDK中会自带一个JRE的,也可以专门独立的只安装JRE,但开发人员一定是安装JDK

3.2 JDK安装

Linux中安装,把下载好的压缩包,直接解压并解归档即可。

例如,解压到当前目录下,也可以解压到指定目录中:

Windows中安装,直接双击安装包,一直下一把即可,默认安装位置:C:\Program Files\Java ,会在此目录中自动创建JDK的目录,并且目录的名字中携带JDK的版本号。

Java-第一章认识和理解Java_第1张图片

注意,安装过程中也会默认安装一个外部的jre,目录名字为jre1.8.0_74

注意,JDK按照目录里面,也自带了一个默认的内部的JRE,点击JDK目录后即可看到此JRE的目录

注意,电脑中可以按照多个不同版本的JDK,不会影响的,之后使用的时候,指定具体的版本即

可。

3.3 环境变量

需要配置三个环境变量,JAVA_HOME、PATH、CLASSPATH,其中PATH是必须,否找不到java的相

关命令所在的文件路径

以Windows系统为例说明:

JAVA_HOME,指定JDK的安装目录,例如,JAVA_HOME=C:\Program Files\Java\jdk1.8.0_74

PATH,把JDK中java命令所在目录配置到原有的PATH中,可以配置到PATH的最前面。例如,

PATH=%JAVA_HOME%\bin;...;...;....; 这里也可以不引用JAVA_HOME的变量值。

CLASSPATH,指定将来要运行加载的class文件所在位置,这个路径将来可能随时变换,可以先配置为当

前路径,将来使用的时候再具体配置即可。例如,CLASSPATH=.

Java-第一章认识和理解Java_第2张图片
Java-第一章认识和理解Java_第3张图片

注意,在Windows中,可以使用where命令,查看java名字所在位置 例如,where java

配置完成之后,在命令窗口中,使用命令进行查看:

java -version

Java-第一章认识和理解Java_第4张图片

注意,如果是Windows系统,配置好了之后,需要重新打开一个新的命令窗口进行测试

注意,如果是Linux系统,配置好了之后,可以先source命令让.bashrc文件生效,或者重写打开新

的命令窗口

3.4 JDK结构

oracle官方文档中提供的JDK结构图:

Java-第一章认识和理解Java_第5张图片

3.5JDK目录

这里以Windows中安装后的目录为例说明:

Java-第一章认识和理解Java_第6张图片
Java-第一章认识和理解Java_第7张图片

关于src.zip文件:

我们将来在代码中,所调用的JavaSE-api中的代码,大多数的源码就存放在这个压缩包中。

这里面都是java文件,这些java文件编译后生成的class文件,都存放在一个jar中,C:\Program

Files\Java\jdk1.8.0_74\jre\lib\rt.jar

我们的代码在运行的之前,JVM会先从这个rt.jar中加载一些我们需要使用的类,例如String、

Object、System等

sun公司程序员--编写基础的代码--》*.java --压缩-- 》src.zip --编译--》 *.class --归档--》rt.jar

一定要记得src.zip和rt.jar中的内容是什么,和它们俩者之间的关系。

4 PI文档

将来在我们编写的代码中,要使用/调用JavaSE-API中所提供的代码,它的源代码存放在src.zip

中,编译后的字节码存放在rt.jar中,这些类、接口的介绍和使用说明,都在JavaSE-API说明文档

中,它就像是一本说明书,来指导我们该如何调用这些别人提供给我们的基本代码。

5 java特点

我们平时所说的“java”,其实是一个综合的描述,它包含了一系列的很多东西。

  • 它是一门编程语言

包含基本的语法、特性、思想等

  • 它是开发环境

开发java程序时,需要一些的java开发工具

  • 它是应用环境

开发好的java程序要运行,需要它提供一些的运行环境

  • 它是部署环境

将来在其他平台下开发出的java程序,都需要它提供一个最基础的部署环境

也就是说,只有是进行和java相关的一系列相关活动,就必须要先安装一个JDK,以保证这些最基

础的环境、工具、语言支持等。

java语言的优点:

  • 更纯粹的面向对象编程,加速开发的过程。

  • 一次编写/编译,到处运行,代码可以跨平台

  • 多线程的支持

  • 代码中没有指针管理、内存管理,使得编程人员可以更加专注于系统的业务功能的开发

java 内存管理

java:面向对象编程语言

手动开辟内存 new

不需要关心内存的释放 GC垃圾回收器

C:面向过程编程语言

malloc() :开辟空间 free() :释放空间

C++(半面向对象编程语言)

new 开辟内存空间,delete():释放空间

注意:说Java是纯面向对象编程语言是错误的,因为它还保留int,double了等基本数据类型

GC垃圾回收器

垃圾:无用的对象内存,没有任何指向的内存。

GC是一个优先级很低的线程实现回收

gc什么时候回收

  1. 内存不够用

  1. 系统闲置的时候

GC回收算法(知道就行)

  1. 标记清扫法

不会整理内存

  1. 内存搬移法

会整理内存,但是耗时

  • 字节码的验证机制,保证代码的安全性

  • 开源及强大的生态环境,社区活跃,第三方类库选择丰富

6 JVM

JVM是Java Virtual Machine(Java虚拟机)的缩写,它是一个虚构出来的计算机规范结构,是通过

在实际的计算机上仿真模拟各种计算机功能来实现的。

JVM是java中最核心的一个东西,它在计算机的内存中,虚拟并提供了java代码可以在其中运行的基础环境。

在JDK7中对JVM规范所给出的内存管理结构如下:

Java-第一章认识和理解Java_第8张图片

和我们程序中关系比较紧密的是堆区、栈区还有方法区,后面我们会经常提及这个几个内存区域

我们将来编写好的java代码,编译成了class文件,这些class文件就需要加载到JVM中,再进行运行

JVM就是java代码和计算机之间的一个桥梁:

Java-第一章认识和理解Java_第9张图片

java代码编译后,计算机并不能直接运行,必须需要经过JVM进行解释后,再进行运行。

所以,java其实并不算是真正的编译语言。

注意,JVM本质上是一个规范,每个公司都可以按照这个规范实现自己的JVM虚拟机

我们现在默认使用的JVM就是oracle公司提供的一个实现,这个JVM叫做HotSpot,从上面的历史介绍

中,也可以找到这个HotSpot虚拟机的来历。

Java-第一章认识和理解Java_第10张图片

当使用java命令来运行程序的时候,会先启动JVM,这个JVM在JDK中对应了一个ddl或so文件:

  • 如果是Windows系统下,这个文件在:%JAVA_HOME%\jre\bin\server\jvm.dll

  • 如果是Linux系统下, 这个文件在:$JAVA_HOME/jre/lib/amd64/server/libjvm.so

7 垃圾回收器

在java语言中,编程人员不需要在代码中控制内存的开辟和释放

java代码中,开辟要使用的内存空间,使用new关键字即可完成。

使用完之后,对内存的释放,在JVM中,由垃圾回收器(Garbage Collection,GC)来完成。

不同类型的GC,在JVM中,会根据不同的算法,对不同的内存区域内标记为垃圾的空间,进行回收释

放。在这个过程中,是不需要编程人员干预的,它自己会主动的完成。

在代码中,我们也可以调用JavaSE-API提供的方法,通知GC现在去进行垃圾回收的工作:

  1. java.lang.System.gc();

  1. java.lang.Runtime.gc();

注意,虽然可以主动通知,但是最后GC并不一定会真的的立刻执行,因为这个垃圾回收的过程什

么时候执行,最终还是要根据GC的具体算法和当前内存的使用情况来确定的

8 字节码验证

编写好的java代码,编译成class文件(字节码)后,再被JVM加载到内存中的时候,是需要做字节

码验证的

编写并编译好的java代码,加载到JVM内存中:******这张图很重要

Java-第一章认识和理解Java_第11张图片

加载到内存的途径有很多,这里描述的是通过网络,也可以是通过本地磁盘

一个class文件被加载到JVM内存之后,首先要经过字节码验证,主要包含:

  • 检查当前class文件的版本和JVM的版本是否兼容

  • 检查当前代码是会破坏系统的完整性

  • 检查当前代码是否有栈溢出的情况

  • 检查当前代码中的参数类型是否正确

  • 检查当前代码中的类型转换操作是否正确

验证通过过,再确定哪些代码是解释执行的,哪些代码是JIT即时编译执行的:

  • 解释执行

class文件内容,需要让JVM进行解释,解释成计算机可以执行的代码。整体效果就是JVM解释一行

代码就执行一行代码。所以如果java代码全是这样的运行方式的话,效率会稍低一些。

  • JIT(Just In Time)即时编译

执行代码的另一种方式,JVM可以把java中的 热点代码 直接编译成计算机可以运行的代码,这样

接下来再调用这个热点代码的时候,就可以直接使用编译好的代码让计算机直接运行,可以提高运

行效率。

9 编写java程序

编译Hello.java中,编写的代码:

Java-第一章认识和理解Java_第12张图片

编译成功后,生成对应的class文件,也就是字节码文件,其实就是0101代码(二进制)

运行Hello.class文件中,编译生成的字节码:

Java-第一章认识和理解Java_第13张图片

字节码,是二进制的0101代码,但是计算机不能直接运行,需要JVM进行解释后再执行

java命令,会先启动JVM虚拟机,然后再进行加载、验证、解释/JIT、运行等一系列的过程

JVM要求的运行规则是,java命令后面加上要运行的类的名字即可,这个类的字节码一定是在class

文件中的,同时要求这个类中,需要有一个程序入口,否则运行失败。

10 常用的命令

  • javac

编译命令

  • java

运行命令

  • javadoc

生成API文档命令

  • javap

反解析命令,可以解析出class字节码文件的内容

  • jar

打包命令

11 常用的包

在程序中,要区分一些东西,一般会采用【命名空间】的设计方式

在java中,package其实就是类的命名空间,用来唯一标识这个类的,同时也把类似功能的类组织

到一个包中

JavaSE-API中常用的包有:

java.lang

最常用的一个包,里面的类可以在我们的程序中直接使用,不需要import导入

java.awt、javax.swing、java.awt.event

这三个包属于同一类型的,它们包下面的类都是用来做图形化界面的(GUI)

注意:javax开头的包,是sun公司后面又扩展进来的包,刚开始是没有的

java.io

这个包下的类主要用于输入输出流的操作。也就是读数据/写数据

java.net

这个包下的类主要用于网络编程。例如把计算机A和计算机B之间建立网络连接,然后进行信息传

输。

java.util

这个包下的类都是一些常用工具类,可以帮我们在代码中完成一些辅助的功能,例如表示日期、使

用集合存储数据、使用工具类操作数组等

理解java☆

Hello.java

public class Hello{
public static void main(String[] args){
    System.out.println("hello world");
    }
}

1 问题

以上面代码为例,展开以下问题的讨论:

问题一:

Hello是我们自己定义的一个类,为什么写java代码的时候要写一个类?

因为在java中,类是组织代码的基本的单元,在类中,可以编写方法,在方法里面,才是真正需要JVM去

执行的一行行的代码,我们平时所说的代码执行,就是这的这些方法中的代码。

java语言中规定,在大多数情况下,需要把执行的代码写在方法中,方法写到类中,所以在写代码的时

候,一般都是先定义一个类,类中定义一个方法,然后把要执行的代码写到方法中。

那么这里所说的类中和方法中,具体的表现形式,就是一对花括号{},如果这对花括号是属于类的,那

么括号里面就表示类中,如果这对花括号属于方法的,那么括号里面就表示方法中。

问题二:

Hello类中的方法,为什么名字要叫做main?

main方法是java中一个特殊的方法,它是作为java程序的唯一入口而存在的。

例如,一个项目中写了好几个百个类,每个类中的方法加起来一共又有上千个方法,那么当JVM加载了

这么多个类和方法的时候,JVM应该从什么地方开始运行程序呢?

java中就使用了一个固定的方法来作为程序的入口,也就是无论写了多少个类,多少个方法,JVM一定是

从这个固定的程序入口方法开始执行代码的,为了能够让JVM很好的识别这个入口方法,这个方法的编

写形式就是固定的:

//这是java中的程序入口方法,一切代码的执行,都要从这里开始
public static void main(String[] args){
//方法中,就可以编写一行行需要运行的代码了
}

所以,我们在Hello类中定义一个main方法的目的就是,让JVM运行Hello这个类的时候,可以直接从类中

的程序入口方法,也就是main开始运行代码,又由于main方法是固定的写法,所以JVM很容易就识别出

来,然后去运行它,main方法被运行了,那么main中的代码就会被一行行的顺序执行了。

思考,如果一个类中没有编写固定形式的main方法,那么使用java命令去运行这个类,会发生什

么?为什么?在这情况下,如果还想使用这个Hello类,该怎么办?

1. 如果Hello中没有固定格式的main方法,使用 java Hello 运行的时候,会报错

2. 原因是因为JVM在Hello类中找不到指定的程序入口,也就是main方法

3. 这个情况下, 还要使用Hello这个的话,就需要在定义其他的一个类,例如Test类,在Test类定义程

序入口main,并且在main方法中调用Hello类中的属性或其他方法,这时候就使用到了Hello类中的

代码了

问题三:

System.out.println("hello world"),这句代码具体是什么意思?

System是JavaSE-API中所提供的一个类,

out是System类中的一个属性,

println是out中的一个方法,

所以,System.out是访问System类中的一个属性,System.out.println是调用out中的一个方法,

而println("hello world")方法的作用,是将字符串"hello world"输出到命令行或控制台中。

可以在API说明文档中,找到这些相关的说明

问题四:

在Hello中,我们为什么可以使用System这个类?

在自己的类中,想使用别人/自己提前写的另一个类,需要以下要求:

这里就以Hello类中使用System类的代码为例说明:

//这是java中的程序入口方法,一切代码的执行,都要从这里开始

public static void main(String[] args){

//方法中,就可以编写一行行需要运行的代码了

}

1 2 3 4 5 6

1. System这个类所在的.java文件已经编写完成,并且已经编译成.class文件

System.java文件在src.zip中,编译好的System.class文件在rt.jar中

2. 这个.class文件所在位置,是JVM可以自动加载的指定路径,这样就可以保证JVM可以把这个.class

文件中的字节码(也就是System这个类的代码),加载到JVM的内存中

JVM在启动的时候,会自动读取rt.jar中所需要的class文件,当然也包括System.class文件

3. 在Hello中,使用import(导入)关键字,将要使用的System类,引入到Hello中,如果System类是

在java.lang包中进行定义的,或者System类和Hello类是在同一个包下面定义的,那么import语句

可以省去不写

当前Hello类中,是没有声明package(包)的,那么这时候Hello类就算是在默认包中,而System

类,是在java.lang包下定义的,所以当前情况下, Hello类中不使用import导入,也可以直接使用

System类。

问题五

使用java Hello运行这个类的时候,JVM是通过什么找到Hello.class文件的?

通过CLASSPATH环境变量配置的路径,来查找的Hello.class文件的,如果配置的路径不对,那么运行Hello

的时候就会报错,告诉我们找不到这个类。

注意,我们之前配置JDK的三个环境变量的时候,把CLASSPATH配置的路径就是点(.),表示当前

路径

问题六:

能否看到JVM去启动运行Hello类之前,确实从rt.jar中加载到了System这个类?

可以,只需要在运行Hello类的时候,加一个参数即可:

java -verbose Hello

verbose参数可以将JVM启动运行的时候加载的信息输出出来,由于内容太多,这里可以做一个输出重定

向:

java -verbose Hello > a.txt

注意,这个操作在Windows里面也是一样的

通过文件中记录的输出内容,就可以看到JVM在运行Hello这个类之前,整个加载的过程和顺序。

注意,这里JVM其实就是给我们自己编写的类Hello,准备运行的环境。

rt.jar中rt就是runtime的缩写,表示运行时环境的意思。

问题六:

Hello这个类的名字和Hello.java的名字有什么必然关系么?

1. 如果文件中的类是public关键字修饰的,那么这个类的名字和java文件的名字就一定要一样

2. 如果文件中的类不是public关键字修饰的,那么这个类的名字和java文件的名字不一样也可以

3. 类名和java文件名字的首字母大写,这是编程规范,大家都是默认遵循这个要求,其实这个字母小

写也没有任何问题

所以,Hello这个类不一定在Hello.java中,但是成功后,Hello这个类一定在Hello.class中

2 pacakge

在java中,定义包的关键字是package

在程序中,要区分一些东西,一般会采用【命名空间】的设计方式,这是大多数语言都会采用的方式。

在java中,如果来区分俩个名字一样的类?例如,张三定义了一个类Hello,李四定义了一个类Hello,当

把张三和李四的代码合并一起的时候,会出现俩个都叫Hello的类,那么这个时候该如果区分这个类?

可以使用package(包)来进行区分,例如张三定义的Hello这个类可以放在zhangsan这个名字的包下,

李四定义的Hello这个类可以放在lisi这个包下,如下:

package zhangsan;
public class Hello{
}
package lisi;
public class Hello{
}

注意,在程序中, package zhangsan; 对应的是一个名字叫zhangsan的文件夹,而 package lisi;

应的是一个名字叫lisi的文件夹,这样俩个代码合并在一起,也完全可以区分开名字相同的俩个Hello类。

但是一般程序中,定义包的时候,不会直接用zhangsanlisi这样的名字,而是都会遵从一些包的命名规

则的:

1. package其实就是类的命名空间,用来唯一标识这个类的,避免和的类的名字重复

2. 一般情况,一个公司、组织、社团中所定义的包的名字,就是他们官网的域名(倒过来),因为

域名一定是全球唯一的,不可能有俩个一样的域名。

例如,http://commons.apache.org/ 这官网下的代码中的包,都是 package

org.apache.commons; 开头的。

例如,你个人写的代码,可以是以 package com.jim; 开头的,假设你的名字叫 jim

3. 类加上了包名,编译之后的效果

这样的类,在编译之后,都必须要有和包名对应的文件夹。

例如, package com.briup.demo;

package zhangsan;
    public class Hello{
    }
    1 2 3 4
    package lisi;
    public class Hello{
    }
    1 2 3 4

这里是三个包,包和包之间用点(.)隔开,编译完之后,需要有对应的三个文件夹分别是

com/briup/demo ,最后在demo目录中,才有编译生产的class文件

一个指定package的类,编译后该如何运行?

例如:

package com.briup.test;
public class Hello{
public static void main(String[] args){
System.out.println("hello world");
}
}

运行一个类的时候,JVM加载这个类的规则是什么?

1. 如果运行的Hello类,没有指定包,Hello类一定对应的是Hello.class ( 固定要求)

那么当运行 java Hello 的时候,JVM会从CLASSPATH中指定的路径中查找,是否有Hello.class这

个文件,如果有那么就加载到内存,然后运行,如果没有那么就报错。

这个情况下,CLASSPATH中就要配置Hello.class文件所在的路径

2. 如果运行的Hello类,指定了包,例如是package com.briup.test; Hello类一定对应的是

com/briup/test/Hello.class(固定要求)

那么当运行 java com.briup.test.Hello 的时候,JVM会从CLASSPATH中指定的路径中查找,

是否有com/briup/test/Hello.class这个文件,如果有那么就加载到内存,然后运行,如果没有那么

就报错。

注意,这个时候JVM从CLASSPATH的路径中,会先找com这个文件夹,然后依次找下去。因为如果

有包存在的时候,这个包就是这个类不可分割的一部分。

这个情况下,CLASSPATH中就要配置com文件夹所在的路径

3. 如果运行的Hello类,被打包到一个jar中,比如是me.jar

那么当运行 java Hello 的时候,JVM会从CLASSPATH中指定的路径中查找,是否有me.jar,如果

有那么就从me.jar中将Hello.class载到内存,然后运行,如果没有那么就报错。(这是Hello没指定

包的情况)

那么当运行 java com.briup.test.Hello 的时候,JVM会从CLASSPATH中指定的路径中查找,

是否有me.jar,如果有那么就从me.jar中将com/briup/test/Hello.class加载到内存,然后运行,如果没有那么就报错。(这是Hello指定包的情况)

注意,这时候,是要把jar文件的路径和jar文件的名字,都配置到CLASSPATH中

3 jar

java中有jar命令,可以将一个或多个class文件,打包到一个指定的jar文件中(xxx.jar)

例如,jre中的rt.jar,就是将src.zip中的java源代码编译成class文件后,又将这些class文件打包到rt.jar中

的。

4 类加载

java中的类,想要运行就必须把类对应的class文件加载到内存,JVM中真正负责加载class文件内容

的是类加载器

在java中,负责把class文件加载到内存的是类加载器(ClassLoader)

JavaSE-API中,有这么一个类: java.lang.ClassLoader ,它就表示JVM中的类加载器。

JVM启动后,默认会有几种类加载器:

启动类加载器 bootstrapClassLoader,非java语言实现

作用:加载指定路径中jar里面的class文件

路径1:C:\Program Files\Java\jdk1.8.0_74\jre\lib\

路径2:C:\Program Files\Java\jdk1.8.0_74\jre\classes\ ( 如果有这个目录的话)

例如:rt.jar

扩展类加载器 ExtClassLoader,java语言实现是ClassLoader类型的对象

作用:加载指定路径中jar里面的class文件( 只能是jar中存在的class)

路径:C:\Program Files\Java\jdk1.8.0_74\jre\lib\ext\

例如:ext中默认存在的jar,或者用户放到ext目录下的jar包

应用类加载器 AppClassLoader,java语言实现是ClassLoader类型的对象

作用:加载指定路径中class文件或者jar里面的class文件

路径:CLASSPATH中配置路径,这个是用户自己配置的

例如:.:bin:hello.jar

我们最常使用的就是应用类加载器,因为它可以通过CLASSPATH中的路径,去加载程序员自己编写

并编译的class文件到内存中。

我们也可以把自己最常用的jar包,放到ext目录中,让扩展类加载器去自动加载这个jar中的class文

件到内存中,这样我们的代码就可以直接使用到这个jar中的类了

但是其实,大多数情况下,即使我们需要用到其他jar中的代码,也一般会把jar所在的路径配置到

CLASSPATH中,让应用类加载器进行加载,这样会更加方便统一管理项目中使用的所有jar

关于启动类加载器,它不是java语言编写的,我们一般也不要去动它的路径或者jar,它是负责在

JVM启动的时候,把JRE环境中最重要的一些library加载到内存,一旦出问题,JVM就无法正常运

行。

5 双亲委托机制

多个类加载器之间共同协作,然后把需要使用或运行的类加载到内存去执行,它们直接共同合作的

方式就是双亲委托机制。

  • 现在要加载Hello.class文件中的类

  • 首先加载任务就交给了AppClassLoader

  • 然后AppClassLoader把这个任务委托给自己的父加载器,也就是ExtClassLoader

  • 然后ExtClassLoader把这个任务委托给自己的父加载器,也就是bootstrapClassLoader

  • 然后bootstrapClassLoader就尝试去加载Hello这个类,但是在指定路径下并没有找到

  • 然后任务又交回给了ExtClassLoader,ExtClassLoader尝试加载Hello这个类,但是在指定路径中没到

  • 然后任务又交回给了AppClassLoader

  • 最后AppClassLoader从CLASSPATH中指定的路径里面找到并加载了这个Hello类,完成类加载的过程

你可能感兴趣的:(Java,java,开发语言,面试)