声明:有一部分内容是摘抄自他人,对此表示感谢,如有冲突,请及时向我指出。
1.使用java开发需要java环境变量,网上教程很多。
配置环境变量的意义:简单的说计算机在执行命令的时候是在环境变量找对应的命令的位置的。如果不正确设置环境变量就不能正确使用相应的命令。如果觉得麻烦可以安装IDE,这样一般就不用自己手动设置环境变量了!
2.java入门阶段包含的知识:
注释、标识符规则、变量(局部变量、成员变量(也可称全局变量)、静态变量),常量和final、基本数据类型、整形变量和整形常量、浮点型变量和BigDecimal、字符型类型、转移字符、布尔型,运算符、算数运算符、赋值运算符、关系运算符、逻辑运算符、位运算符、字符串连接符、条件运算符(min=(a<b)?a:b)、自动类型转换、强制类型转换、使用Scanner获得键盘输入、控制语句、if else if、while、for、break、switch、嵌套循环、continue、带标签的break(Java中的标签就是一个紧跟着“:”的标识符。用于跳出标签所处的循环之前的循环)、方法的定义(形参、实参、返回值、语句块)、重载、递归算法(无限调用自身)。
对相关知识点进行详细分析:
-
关键字保留字说明如下:
Java关键字列表 (依字母排序 共50组):
abstract, assert,
boolean, break, byte,
case, catch, char, class, const, continue,
default, do, double,
else, enum, extends,
final, finally, float, for,
goto,
if, implements, import, instanceof, int, interface,
long,
native, new,
package, private, protected, public,
return,
short, static, strictfp, super, switch, synchronized,
this, throw, throws, transient, try,
void, volatile,
while
其中:
保留关键字为:goto, const
Java1.2添加的关键字:strictfp
Java1.4添加的关键字:assert
Java5.0添加的关键字:enum -
Java保留字列表 (依字母排序)
Java保留字是指现有Java版本尚未使用,但以后版本可能会作为关键字使用:
byValue,cast, const,future,generic, gotoinner,null,operator, outer,rest,var
详细了解各个关键字和保留字的含义可看下面网址:https://blog.youkuaiyun.com/qq_37933685/article/details/81282182 -
运算符比较(由高到低):
3.java面向对象基础:
包括面向过程和面向对象的区别、对象、类、内存、堆栈、方法区、构造方法、垃圾回收机制、this的本质、static关键字、静态初始化块、java参数传值机制、Java包机制、import详解、继承instanceof的使用、静态导入import(很少使用)、方法的重写override(方法继承)、重载(参数不同、同名)、Object类、equals方法的重写、super父类对象引用、封装、多态、对象的转型(向上转型和向下转向)、final修饰变量和方法和类、数组的使用、数据的3中初始化方式(静态初始化、动态初始化、默认初始化)、数组的遍历(foreach循环)。
下面对上述的基础知识进行部分详细分析:
-
我对面向过程和面向对象的理解:
(1)面向过程是将整个任务按照流程步骤划分,一步一步从上下向下(通过方法)实现。效率高
(2)面向对象是将整个任务分成多个对象,让对象来实现整个流程。效率低 -
Java final 关键词修饰类、方法、变量
-
final修饰类
被修饰的类不能被继承,也没有子类。假如随意创建这些类的子类,子类可能会错误的修改父类的实现细节、出于安全原因,类的实现细节不允许有任何改动、在创建对象模型的时候,确信这个类不会再被扩展。注:如果对一个已经被final修饰的类进行继承操作,则会在编译的期间直接出现错误 -
final修饰方法
final修饰的方法表示此方法已经是“最后的、最终的”含义,亦即此方法不能被重写,(但是可以载多个final修饰的方法)。
需要注意的一点是: 重写的前提是子类可以从父类中继承此方法,如果父类中final修饰的方法的访问权限是private,将会导致子类不能直接继承到此方法,这时候再在子类中定义相同的方法名和参数,不会产生重写与final之间出现的矛盾,而是在子类中定义了新的方法。 -
final修饰变量
final修饰的变量表示此变量是“最后的、最终的”,一旦定义了final变量,并在首次为其显示进行了初始化之后,final修饰的变量值不可被再修改。
final修饰的变量,无论是类属性、对象属性、形参还是局部变量,这些变量都是需要进行显示初始化(即为其显示指定初始值)。
另外,无论对于基本数据类型还是引用数据类型,final修饰的变量都是首次初始化后值都不能修改。对于基本数据类型,很好理解。对于引用
数据类型,引用变量指向的是实际的对象,但其存储的是所指向对象的地址,因此,其值不能修改并不意味着其所指向的对象不能修改。
-
我对JVM的理解:
(1)JVM虚拟机内部体系结构
(2)堆:堆中存放的是所有在java程序运行过程中创建的对象,数组也在堆中。垃圾回收主要工作的目标区域就是堆。
(3)方法区:方法区中存放了所有类装载进来后和这个类相关的所有运行时需要的信息(如类的静态变量、常量、类的全局名称、方法信息等)。
(4)和前面介绍的两个区域的所有线程共享的不同,Java栈额本地方法栈以及PC寄存器都是线程独占的,也就是说每个线程都有一个Java栈和PC寄存器或者本地方方法栈(如果用到了本地方法的话)。
说到这里需要介绍一下本地方法,我们知道java是跨平台的,但是我们比如在需要读文件的时候,不用去关心将来是在哪个平台运行,只要调用FileInputStream把文件读入就可以了,不用调用底层操作系统的API函数,这是因为不同平台Java的API把所有这些与平台相关的操作都封装了起来提供了一个统一的Java编程接口。而Java的API正是通过调用一些本地方法(这些方法很多时候是一些编译后的可执行的C程序)来实现了这些功能。同时虽然Java实现了大部分平台都有的一些功能(如IO,多线程等),但是有些平台的一些功能是该平台特有的,提供Java虚拟机的厂商为了提供这些功能往往就以动态链接库的形式提供一些本地方法的调用来完善JVM在该平台的功能。至于如何去调用以及如何与本地方法通信(获取返回值等)就是具体JVM实现需要去做的事情。说了这么多本地方法的内容,现在回到Java栈的部分,每个线程都有一个自己独立的Java栈,每次线程执行到一个新的方法时就在栈里面压入一个栈帧。帧里包含了方法里的局部变量,操作数栈以及帧数据区。这三种区域中局部变量很好理解,就是在方法作用范围内的变量,包括基本变量和对象的引用。理解操作数栈要先对JVM执行java程序的过程有所了解,JVM在装载进class文件后可能采用解释执行、即时编译执行、混合执行这三种方式来执行class文件中的JVM指令集。JVM指令集是一个4字节的指令集,就像汇编语言做相加操作需要先将两个数存入寄存器一样,JVM指令做数据相关的操作也要先将数据压入java栈里面的操作数栈才能进行。比如方法里将i变量和j变量相加赋值给z,JVM先将i压入操作数栈,再将j压入操作数栈,最后将结果写回局部变量表或者是对象的字段。至于帧数据区,是为了在方法执行过程中访问方法区的数据以及返回方法结果而用的。某个方法执行结束完之后如果是正常返回则会将返回结果压入上一个方法的操作数栈中,如果是异常退出且没有catch该异常则会运行到上一个方法继续抛出该异常。
本地方法和Java方法一样,只是Java栈是执行Java方法的线程申请的内存,而本地方法是执行本地方法而申请的内存。下面这张图显示了两者的关系。
(5)本地方法栈及native方法
简单地讲,一个Native Method就是一个java调用非java代码的接口。
为什么要使用Native Method?
我们知道java是高级编程语言,当对一些底层的如操作系统或某些硬件交换信息时,我们使用java来编程实现起来不容易,再者使用java来编程效率也很低下。这就不得不需要调用本地方法来解决这一问题。
与java环境外交互:
有时java应用需要与java外面的环境交互。这是本地方法存在的主要原因,你可以想想java需要与一些底层系统如操作系统或某些硬件交换信息时的情况。本地方法正是这样一种交流机制:它为我们提供了一个非常简洁的接口,而且我们无需去了解java应用之外的繁琐的细节。
与操作系统交互:
JVM支持着java语言本身和运行时库,它是java程序赖以生存的平台,它由一个解释器(解释字节码)和一些连接到本地代码的库组成。然而不管怎 样,它毕竟不是一个完整的系统,它经常依赖于一些底层(underneath在下面的)系统的支持。这些底层系统常常是强大的操作系统。通过使用本地方法,我们得以用java实现了jre的与底层系统的交互,甚至JVM的一些部分就是用C写的,还有,如果我们要使用一些java语言本身没有提供封装的操作系统的特性时,我们也需要使用本地方法。
Sun’s Java
Sun的解释器是用C实现的,这使得它能像一些普通的C一样与外部交互。jre大部分是用java实现的,它也通过一些本地方法与外界交互。例如:类java.lang.Thread 的 setPriority()方法是用java实现的,但是它实现调用的是该类里的本地方法setPriority0()。这个本地方法是用C实现的,并被植入JVM内部,在Windows 95的平台上,这个本地方法最终将调用Win32 SetPriority() API。这是一个本地方法的具体实现由JVM直接提供,更多的情况是本地方法由外部的动态链接库(external dynamic link library)提供,然后被JVM调用。
(6)垃圾回收机制:
垃圾回收可以有效的防止内存泄露,有效的使用空闲的内存;
先说下内存泄漏和内存溢出:
内存泄露:指该内存空间使用完毕后未回收,在不涉及复杂数据结构的一般情况下,java的内存泄露表现为一个内存对象的生命周期超出了程序需要它的时间长度,我们有是也将其称为“对象游离”;
内存溢出:内存溢出分三种情况:
1*、OutOfMemoryError: PermGen space
Permanent Generation space 这个区域主要用来保存加来的Class的一些信息,在程序运行期间属于永久占用的,Java的GC不会对他进行释放,所以如果启动的程序加载的信息比较大,超出了这个空间的大小,就会发生溢出错误;
解决的办法无非就是增加空间分配了——增加java虚拟机中的XX:PermSize和XX:MaxPermSize参数的大小,其中XX:PermSize是初始永久保存区域大小,XX:MaxPermSize是最大永久保存区域大小。
2*、OutOfMemoryError:Java heap space
heap 是Java内存中的堆区,主要用来存放对象,当对象太多超出了空间大小,GC又来不及释放的时候,就会发生溢出错误。
Java中对象的创建是可控的,但是对象的回收是由GC自动的,一般来说,当已存在对象没有引用(即不可达)的时候,GC就会定时的来回收对象,释放空间。但是因为程序的设计问题,导致对象可达但是又没有用(即前文提到的内存泄露),当这种情况越来越多的时候,问题就来了。
针对这个问题,我们需要做一下两点:
1、检查程序,减少大量重复创建对象的死循环,减少内存泄露。
2、增加Java虚拟机中Xms(初始堆大小)和Xmx(最大堆大小)参数的大小。
3*、StackOverFlowError
stack是Java内存中的栈空间,主要用来存放方法中的变量,参数等临时性的数据的,发生溢出一般是因为分配空间太小,或是执行的方法递归层数太多创建了占用了太多栈帧导致溢出。
针对这个问题,除了修改配置参数-Xss参数增加线程栈大小之外,优化程序是尤其重要。
垃圾回收机制算法:
垃圾回收器主要回收的是堆区中未使用的内存区域,并对相应的区域进行整理。在堆区中,又根据对象内存的存活时间或者对象大小,分为“年轻代”和“年老代”。“年轻代”中的对象是不稳定的易产生垃圾,而“年老代”中的对象比较稳定,不易产生垃圾。之所以将其分开,是分而治之,根据不同区域的内存块的特点,采取不同的内存回收算法,从而提高堆区的垃圾回收的效率。下方会给出具体的介绍。
详细内容参:https://www.cnblogs.com/ludashi/p/6694965.html
常见的内存回收算法简介
上面我们简单的了解的JVM中内存区域的划分,接下来我们就来看一下几种常见的内存回收算法。当然,下方所介绍的内存回收的算法不仅仅是JVM中所使用到的,我们还会回顾一下OC中的内存回收方式。下方主要包括“引用计数式回收”、“复制式回收”、“标记整理式回收”、“分代式回收”。
JVM(HotSpot) 7种垃圾收集器的特点及使用场景:
上图展示了7种作用于不同分代的收集器,如果两个收集器之间存在连线,就说明它们可以搭配使用。
1.Serial收集器
Serial收集器是最基本、发展历史最悠久的收集器。是单线程的收集器。它在进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集完成。
2.ParNew收集器
ParNew收集器其实就是Serial收集器的多线程版本,除了使用多线程进行垃圾收集之外,其余行为包括Serial收集器可用的所有控制参数、收集算法、Stop The Worl、对象分配规则、回收策略等都与Serial 收集器完全一样。
3.Parallel Scavenge(并行回收)收集器
Parallel Scavenge收集器是一个新生代收集器,它也是使用复制算法的收集器,又是并行的多线程收集器。
4.Serial Old 收集器
Serial Old是Serial收集器的老年代版本,它同样是一个单线程收集器,使用标记整理算法。这个收集器的主要意义也是在于给Client模式下的虚拟机使用。
5.Parallel Old 收集器
Parallel Old 是Parallel Scavenge收集器的老年代版本,使用多线程和“标记-整理”算法。这个收集器在1.6中才开始提供。
6.CMS收集器
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。目前很大一部分的Java应用集中在互联网站或者B/S系统的服务端上,这类应用尤其重视服务器的响应速度,希望系统停顿时间最短,以给用户带来较好的体验。CMS收集器就非常符合这类应用的需求
7.G1收集器
G1收集器的优势:
(1)并行与并发
(2)分代收集
(3)空间整理 (标记整理算法,复制算法)
(4)可预测的停顿(G1处处理追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒,这几乎已经实现Java(RTSJ)的来及收集器的特征)
详细内容见:https://www.cnblogs.com/chengxuyuanzhilu/p/7088316.html
jdk1.7 默认垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代)
jdk1.8 默认垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代)
jdk1.9 默认垃圾收集器G1
-XX:+PrintCommandLineFlagsjvm参数可查看默认设置收集器类型
-XX:+PrintGCDetails亦可通过打印的GC日志的新生代、老年代名称判断
再说一下分代垃圾回收:
分代垃圾回收机制,是基于这样一个事实:不同的对象的生命周期是不一样的。因此,不同生命周期的对象可以采取不同的回收算法,以便提高回收效率。我们将对象分为三种状态:年轻代、年老代、持久代。JVM将堆内存划分为 Eden、Survivor 和 Tenured/Old 空间。
1. 年轻代
所有新生成的对象首先都是放在Eden区。 年轻代的目标就是尽可能快速的收集掉那些生命周期短的对象,对应的是Minor GC,每次 Minor GC 会清理年轻代的内存,算法采用效率较高的复制算法,频繁的操作,但是会浪费内存空间。当“年轻代”区域存放满对象后,就将对象存放到年老代区域。
2. 年老代
在年轻代中经历了N(默认15)次垃圾回收后仍然存活的对象,就会被放到年老代中。因此,可以认为年老代中存放的都是一些生命周期较长的对象。年老代对象越来越多,我们就需要启动Major GC和Full GC(全量回收),来一次大扫除,全面清理年轻代区域和年老代区域。
3. 持久代
用于存放静态文件,如Java类、方法等。持久代对垃圾回收没有显著影响。
·Minor GC:
用于清理年轻代区域。Eden区满了就会触发一次Minor GC。清理无用对象,将有用对象复制到“Survivor1”、“Survivor2”区中(这两个区,大小空间也相同,同一时刻Survivor1和Survivor2只有一个在用,一个为空)
·Major GC:
用于清理老年代区域。
·Full GC:
用于清理年轻代、年老代区域。 成本较高,会对系统性能产生影响。
垃圾回收过程:
1、新创建的对象,绝大多数都会存储在Eden中,
2、当Eden满了(达到一定比例)不能创建新对象,则触发垃圾回收(GC),将无用对象清理掉,
然后剩余对象复制到某个Survivor中,如S1,同时清空Eden区
3、当Eden区再次满了,会将S1中的不能清空的对象存到另外一个Survivor中,如S2,
同时将Eden区中的不能清空的对象,也复制到S1中,保证Eden和S1,均被清空。
4、重复多次(默认15次)Survivor中没有被清理的对象,则会复制到老年代Old(Tenured)区中,
5、当Old区满了,则会触发一个一次完整地垃圾回收(FullGC),之前新生代的垃圾回收称为(minorGC)
要点:
1. 程序员无权调用垃圾回收器。
2. 程序员可以调用System.gc(),该方法只是通知JVM,并不是运行垃圾回收器。尽量少用,会申请启动Full GC,成本高,影响系统性能。
3. finalize方法,是Java提供给程序员用来释放对象或资源的方法,但是尽量少用。