java概述
特性
1.简单
语法规则和c++类似,是对C++的简化和提高,使用接口取代了多重继承,并取消了指针,java还有垃圾自动收集。
提供了丰富的类库,API文档和第三方开发包,还有大量基于java 的开源项目。
2.面向对象(Object Oriented)
是对现实世界的一种抽象,面向对象会把相关的数据和方法组织为一个整体来看待。所有的元素都要通过类和对象来访问。
3.分布性
Java语言支持Internet应用的开发,java中有net.api,他提供了用于网络应用编程的类库,包括URL、URLConnection、Socket、ServerSocket等。Java的RMI(远程方法激活)机制也是开发分布式应用的重要手段。
包括操作分布和数据分布。操作分布是指在多个不同的主机上布置相关操作,而数据分布是将数据分别存放在多个不同的主机上。
4.平台独立性和可移植性(write once ,run anywhere)
核心是jvm。
在应用中编写java代码,用Eclipse或javac把java代码编译为.class文件,然后把.class文件打成.jar文件,.jar文件可以在windows、MacOS、Linux系统下运行。
5.解释性
运行java程序需要解释器,任何移植了java解释器的计算机或其他设备都可以用java字节码进行解释运行。
6. 安全性
删除了类C语言中的指针和内存释放等语法,有效避免了用户对内存的非法操作。Java程序代码要经过代码校验,指针校验等很多测试步骤才能运行。
7. 健壮性
Java的强类型机制,异常处理,垃圾的自动收集等。开发工具如:Eclipse、NetBeans。
8.多线程
Java在用户空间实现的多线程(实现多线程的方式有:在用户空间、在内核空间、在用户和内核空间中混合实现)应用程序在同一时间并行执行多项任务,而且相应的同步机制可以保证不同的线程可以正确的共享数据。
9.高性能
Hotspot JVM提供了JIT(just in time)编译器即动态编译器,能够在运行时将代码编译为机器码,运行效率比较高。
10.动态
可以动态调整库内方法和增加变量,而客户端不需要作任何更改。
11.开源
java的这个特性决定了它的广泛应用。
运行和工作原理
Java源代码(HelloWorld.java) –> java字节码文件(HelloWorld.class)–> 机器码 –> 运行结果
1.首先编写java源代码程序,扩展名为.java
2.在命令行模式中,输入javac命令:javac 源文件名.java
对源代码进行编译,生成字节码文件,扩展名为.class
3.编译完成后,如果没有报错信息,输入java命令:java 类名
对class字节码文件进行解释运行,执行时不需要添加.class扩展名
这里是我最早接触java时,还没有用到开发工具,在cmd命令行进行的HelloWorld输出。当时踩得坑如下:
文件隐藏了扩展名
javac+文件名,java+类名 (所以当时需要文件名和类名一致)
严格区分大小写
非法字符:/65307确是中文问题,要保证全部英文状态
括号成对
最后贴一个梦开始的main方法:
public static void main(String[] args){
System.out.println(“HelloWorld”);
}
版本
版本 | 名称 | 发行日期 |
---|---|---|
JDK 1.0 | Oak(橡树) | 1996-01-23 |
JDK 1.1 | none(无) | 1997-02-19 |
JDK 1.1.4 | Sparkler(宝石) | 1997-09-12 |
JDK 1.1.5 | Pumpkin(南瓜) | 1997-12-13 |
JDK 1.1.6 | Abigail(阿比盖尔–女子名) | 1998-04-24 |
JDK 1.1.7 | Brutus(布鲁图–古罗马政治家和将军) | 1998-09-28 |
JDK 1.1.8 | Chelsea(切尔西–城市名) | 1999-04-08 |
J2SE 1.2 | Playground(运动场) | 1998-12-04 |
J2SE 1.2.1 | none(无) | 1999-03-30 |
J2SE 1.2.2 | Cricket(蟋蟀) | 1999-07-08 |
J2SE 1.3 | Kestrel(美洲红隼) | 2000-05-08 |
J2SE 1.3.1 | Ladybird(瓢虫) | 2001-05-17 |
J2SE 1.4.0 | Merlin(灰背隼) | 2002-02-13 |
J2SE 1.4.1 | grasshopper(蚱蜢) | 2002-09-16 |
J2SE 1.4.2 | Mantis(螳螂) | 2003-06-26 |
Java SE 5.0 (1.5.0) | Tiger(老虎) | 2004-09-30 |
Java SE 6.0 (1.6.0) | Mustang(野马) | 2006-04 |
Java SE 7.0 (1.7.0) | Dolphin(海豚) | 2011-07-28 |
Java SE 8.0 (1.8.0) | Spider(蜘蛛) | 2014-03-18 |
Java SE 9.0 | none(无) | 2017-09-21 |
Java SE 10.0 | none(无) | 2018-03-21 |
Java SE 11.0 | none(无) | 2018-09-25 |
这里的JAVA就是指JDK开发工具
Java2
1998年12月8日,Sun公司发布了第二代Java平台(简称为Java2)的3个版本
J2ME(Java2 Micro Edition,Java2平台的微型版):主要用于嵌入式系统的开发,应用于移动、无线及有限资源的环境
J2SE(Java 2 Standard Edition,Java 2平台的标准版):应用于桌面应用软件的编程
J2EE(Java 2 Enterprise Edition,Java 2平台的企业版):应用于基于Java的应用服务器
Java5
2004年9月30日
1.自动拆装箱
2.泛型
3.增强for
说明:是for循环的一种,简化数组和Collection集合的遍历(增强for就是来替代迭代器的)
格式:
for(元素数据类型 变量 : 数组或者Collection集合) {
使用变量即可,该变量就是元素
}
注意事项:增强for的目标要先进行不为null的判断,然后再使用
4.静态导入
说明:可以直接导入到方法级别
格式:import static 包名….类名.方法名;
注意事项:
方法必须是静态的
如果有多个同名的静态方法,容易不知道使用谁?这个时候要使用的话,就必须在调用前面加静态的包,类名前缀。由此可见,意义不大,所以一般不用。
5.可变参数
说明:定义方法的时候不知道该定义多少个参数,当调用的时候才知道需要几个参数。
格式:
public static int sunm(int… a){}
修饰符 返回值类型 方法名(数据类型… 变量名){
}
注意事项:
1.这里的变量(a)其实是一个数组
2.如果一个方法有可变参数,并且有多个参数,那么,可变参数肯定是最后一个(如果不是最后一个,相当于前面的都包含了,最后单独的多出来了,所以就会报错。只有当可变参数是最后一个才是正常的)
3.如果要调用的方法可以和两个可变参数匹配,则出现错误
4.在调用方法的时候,如果能够和固定参数的方法匹配,也能够与可变长参数的方法匹配,则选择固定参数的方法
Arrays工具类中的一个方法:把数组转成集合
public static <T> List<T> asList(T... a)
注意事项:集合的长度不能改变,因为其本质还是一个数组。(所以对集合进行操作时,只要改变了其长度就会报错)
6.枚举
Java7
1.二进制字面量
JDK7开始,可以用二进制来表示整数(byte,short,int和long)。
好处:可以使代码更容易被理解。语法非常简单,只要在二进制数值前面加 0b或者0B
2.数字字面量可以出现下划线
为了增强对数值的阅读性,如我们经常把数据用逗号分隔一样。JDK7提供了_对数据进行分隔。
注意事项:
不能出现在进制标识和数值之间
不能出现在数值开头和结尾
不能出现在小数点旁边
3.switch 语句可以用字符串
4.泛型简化(泛型推断)
5.异常的多个catch合并
出现了一个新的异常处理方案:
try {
} catch (异常名1 | 异常名2 | 异常名3 … 变量 ) {
}
这个方式虽然简洁,但是也不够好:
A:处理方式是一致的。(实际开发中,好多时候可能就是针对同类型的问题,给出同一个处理)
B:多个异常间必须是平级关系
6.try-with-resources 语句
try ( 必须是java.lang.AutoCloseable的子类对象 ) { … }
好处:
资源自动释放,不需要close()了
把需要关闭资源的部分都定义在()中就ok了
这个接口的子类主要是流体系的对象(JDK7的API中AutoCloseable的子类)
Java8
2014年3月18日,普遍使用的版本,是java5以来最具革命性的版本。支持32位
1.允许接口中有默认方法,静态方法实现
2.Lambda表达式
一、引言
java8最大的特性就是引入Lambda表达式,即函数式编程,可以将行为进行传递。总结就是:使用不可变值与函数,函数对不可变值进行处理,映射成另一个值。
二、Java重要的函数式接口
1、什么是函数式接口
函数接口是只有一个抽象方法的接口,用作 Lambda 表达式的类型。使用@FunctionalInterface注解修饰的类,编译器会检测该类是否只有一个抽象方法或接口,否则,会报错。可以有多个默认方法,静态方法。
1.1 java8自带的常用函数式接口。
1 | public class Test { |
以上演示了lambda接口的使用及自定义一个函数式接口并使用。下面,我们看看java8将函数式接口封装到流中如何高效的帮助我们处理集合。
注意:Student::getName例子中这种编写lambda表达式的方式称为方法引用。格式为ClassNmae::methodName。是不是很神奇,java8就是这么迷人。
示例:本篇所有示例都基于以下三个类。OutstandingClass:班级;Student:学生;SpecialityEnum:特长。
1.2 惰性求值与及早求值
惰性求值:只描述Stream,操作的结果也是Stream,这样的操作称为惰性求值。惰性求值可以像建造者模式一样链式使用,最后再使用及早求值得到最终结果。
及早求值:得到最终的结果而不是Stream,这样的操作称为及早求值。
2、常用的流
2.1 collect(Collectors.toList())
将流转换为list。还有toSet(),toMap()等。及早求值。
1 | public class TestCase { |
2.2 filter
顾名思义,起过滤筛选的作用。内部就是Predicate接口。惰性求值。
比如我们筛选出出身高小于180的同学。
1 | public class TestCase { |
2.3 map
转换功能,内部就是Function接口。惰性求值
1 | public class TestCase { |
例子中将student对象转换为String对象,获取student的名字。
2.4 flatMap
将多个Stream合并为一个Stream。惰性求值
1 | public class TestCase { |
调用Stream.of的静态方法将两个list转换为Stream,再通过flatMap将两个流合并为一个。
2.5 max和min
我们经常会在集合中求最大或最小值,使用流就很方便。及早求值。
1 | public class TestCase { |
max、min接收一个Comparator(例子中使用java8自带的静态函数,只需要传进需要比较值即可。)并且返回一个Optional对象,该对象是java8新增的类,专门为了防止null引发的空指针异常。
可以使用max.isPresent()判断是否有值;可以使用max.orElse(new Student()),当值为null时就使用给定值;也可以使用max.orElseGet(() -> new Student());这需要传入一个Supplier的lambda表达式。
2.6 count
统计功能,一般都是结合filter使用,因为先筛选出我们需要的再统计即可。及早求值
1 | public class TestCase { |
2.7 reduce
reduce 操作可以实现从一组值中生成一个值。在上述例子中用到的 count 、 min 和 max 方法,因为常用而被纳入标准库中。事实上,这些方法都是 reduce 操作。及早求值。
1 | public class TestCase { |
我们看得reduce接收了一个初始值为0的累加器,依次取出值与累加器相加,最后累加器的值就是最终的结果。
三、高级集合类及收集器
3.1 转换成值
收集器,一种通用的、从流生成复杂值的结构。只要将它传给 collect 方法,所有的流就都可以使用它了。标准类库已经提供了一些有用的收集器,以下示例代码中的收集器都是从 java.util.stream.Collectors 类中静态导入的。
1 | public class CollectorsTest { |
maxBy或者minBy就是求最大值与最小值。
3.2 转换成块
常用的流操作是将其分解成两个集合,Collectors.partitioningBy帮我们实现了,接收一个Predicate函数式接口。
将示例学生分为会唱歌与不会唱歌的两个集合。
1 | public class PartitioningByTest { |
3.3 数据分组
数据分组是一种更自然的分割数据操作,与将数据分成 ture 和 false 两部分不同,可以使用任意值对数据分组。Collectors.groupingBy接收一个Function做转换。
如图,我们使用groupingBy将根据进行分组为圆形一组,三角形一组,正方形一组。
例子:根据学生第一个特长进行分组
1 | public class GroupingByTest { |
Collectors.groupingBy与SQL 中的 group by 操作是一样的。
3.4 字符串拼接
如果将所有学生的名字拼接起来,怎么做呢?通常只能创建一个StringBuilder,循环拼接。使用Stream,使用Collectors.joining()简单容易。
1 | public class JoiningTest { |
joining接收三个参数,第一个是分界符,第二个是前缀符,第三个是结束符。也可以不传入参数Collectors.joining(),这样就是直接拼接。
3.函数式接口
4.内置函数式接口
5.Streams
得益于Lambda所带来的函数式编程,引入了一个全新的Stream概念,用于解决已有集合类库既有的弊端
传统集合在于使用循环遍历。for循环的语法就是“怎么做”,for循环的循环体才是“做什么”。
Java 8的Lambda让我们可以更加专注于做什么(What),而不是怎么做(How)
1 | import java.util.ArrayList; |
“Stream流”其实是一个集合元素的函数模型,它并不是集合,也不是数据结构,其本身并不存储任何 元素(或其地址值
获取流:
所有的 Collection 集合都可以通过 stream 默认方法获取流;
Stream 接口的静态方法 of 可以获取数组对应的流
根据Collection获取流:
1 | public static void main(String[] args) { |
根据Map获取流
1 | public static void main(String[] args) { |
根据数组获取流
1 | public static void main(String[] args) { |
常用方法
逐一处理:forEach 会将每一个流元素交给该函数进行处理
1 | public static void main(String[] args) { |
过滤:filter 可以通过 filter 方法将一个流转换成另一个子集流
1 | public static void main(String[] args) { |
映射:map 如果需要将流中的元素映射到另一个流中
1 | public static void main(String[] args) { |
统计个数:count 数一数其中的元素个数
1 | public static void main(String[] args) { |
取用前几个:limit 可以对流进行截取,只取用前n个
1 | public static void main(String[] args) { |
跳过前几个:skip 如果希望跳过前几个元素
1 | public static void main(String[] args) { |
组合:concat 如果有两个流,希望合并成为一个流
1 | public static void main(String[] args) { |
一个List泛型根据另一个List泛型的属性进行过滤
1 | List<User> list1 = new ArrayList<>(); |
6.Map
7. 时间日期API
Java8以前,我们一直长期使用Date和Calendar来处理时间,而在使用Date处理日期时间问题上会存在一定的隐患,产生线程不安全的问题,最典型的就是在一定负载并发量的情况下使用SimpleDateFormat引发的线程安全性问题。如今Java8提供了LocalDate、LocalTime、LocalDateTime三个日期时间类,在安全性和操作性上对比Date和Calendar非常可观
- 使用Date输出的日期可读性差(在不进行日期格式化的情况下)
Tue Sep 10 09:34:04 CST 2019
- 在对Date使用SimpleDateFormat进行日期时间格式化时我们需要明确的知道,SimpleDateFormat是线程不安全的,在高并发高负载的情况下使用,极容易引发线程安全性问题
calendar是共享变量,并且这个共享变量没有做线程安全控制。当多个线程同时使用相同的SimpleDateFormat对象【如用static修饰的SimpleDateFormat】调用format方法时,多个线程会同时调用calendar.setTime方法,可能一个线程刚设置好time值另外的一个线程马上把设置的time值给修改了导致返回的格式化时间可能是错误的。SimpleDateFormat除了format是线程不安全以外,parse方法也是线程不安全的。parse方法实际调用alb.establish(calendar).getTime()方法来解析,alb.establish(calendar)方法里主要完成了以下操作(这三步不是原子性操作):
重置日期对象cal的属性值
使用calb中中属性设置cal
返回设置好的cal对象
多线程并发如何保证线程安全
避免线程之间共享一个SimpleDateFormat对象,每个线程使用时都创建一次SimpleDateFormat对象(问题:创建和销毁对象时大量内存资源开销)
对使用format和parse方法的地方进行加锁(问题:线程阻塞性能差)
使用ThreadLocal保证每个线程最多只创建一次SimpleDateFormat对象,属于较好的方法。
3. Date对时间处理比较麻烦
比如想获取某年、某月、某星期,以及n天以后的时间,使用Date来处理的话会特别麻烦,你可能会说Date类不是有getYear、getMonth这些方法吗,获取年月日很Easy,但都被JDK弃用了。
LocalDate:年月日
LocalTime:时分秒
LocalDateTime:年月日时分秒
- LocalDate
//获取当前年月日
LocalDate localDate = LocalDate.now();
//构造指定的年月日
LocalDate localDate1 = LocalDate.of(2019, 9, 10);
//获取年、月、日、星期几
int year = localDate.getYear();
int year1 = localDate.get(ChronoField.YEAR);
Month month = localDate.getMonth();
int month1 = localDate.get(ChronoField.MONTH_OF_YEAR);
int day = localDate.getDayOfMonth();
int day1 = localDate.get(ChronoField.DAY_OF_MONTH);
DayOfWeek dayOfWeek = localDate.getDayOfWeek();
int dayOfWeek1 = localDate.get(ChronoField.DAY_OF_WEEK);
2. LocalTime
// 获取当前时间
LocalTime localTime1 = LocalTime.now();
// 获取指定时间
LocalTime localTime = LocalTime.of(13, 51, 10);
//获取小时
int hour = localTime.getHour();
int hour1 = localTime.get(ChronoField.HOUR_OF_DAY);
//获取分
int minute = localTime.getMinute();
int minute1 = localTime.get(ChronoField.MINUTE_OF_HOUR);
//获取秒
int second = localTime.getMinute();
int second1 = localTime.get(ChronoField.SECOND_OF_MINUTE);
- LocalDateTime
等同于LocalDate+LocalTime
// 获取当前日期 年月日时分秒
LocalDateTime localDateTime = LocalDateTime.now();
// 设置指定日期时间 年月日时分秒
LocalDateTime localDateTime1 = LocalDateTime.of(2019, Month.SEPTEMBER, 10, 14, 46, 56);
LocalDateTime localDateTime2 = LocalDateTime.of(localDate, localTime);
LocalDateTime localDateTime3 = localDate.atTime(localTime);
LocalDateTime localDateTime4 = localTime.atDate(localDate);
LocalDate localDate2 = localDateTime.toLocalDate();
LocalTime localTime2 = localDateTime.toLocalTime();
4. Instant(获取秒数)
Instant instant = Instant.now();
long currentSecond = instant.getEpochSecond();
long currentMilli = instant.toEpochMilli();
注:个人觉得如果只是为了获取秒数或者毫秒数,使用System.currentTimeMillis()来得更为方便
LocalDate、LocalTime、LocalDateTime、Instant为不可变对象,修改这些对象对象会返回一个副本。比如增加、减少年数、月数、天数等,以LocalDateTime为例:
LocalDateTime localDateTime = LocalDateTime.of(2019, Month.SEPTEMBER, 10,
14, 46, 56);
//增加一年
localDateTime = localDateTime.plusYears(1);
localDateTime = localDateTime.plus(1, ChronoUnit.YEARS);
//减少一个月
localDateTime = localDateTime.minusMonths(1);
localDateTime = localDateTime.minus(1, ChronoUnit.MONTHS);
通过with修改某些值
//修改年为2019
localDateTime = localDateTime.withYear(2020);
//修改为2022
localDateTime = localDateTime.with(ChronoField.YEAR, 2022);
还可以修改月、日,比如有些时候想知道这个月的最后一天是几号、下个周末是几号
LocalDate localDate = LocalDate.now();
//比如通过firstDayOfYear()返回了当前日期的第一天日期,还有很多方法这里不在举例说明
LocalDate localDate1 = localDate.with(firstDayOfYear());
格式化时间
LocalDate localDate = LocalDate.of(2019, 9, 10);
String s1 = localDate.format(DateTimeFormatter.BASIC_ISO_DATE);
String s2 = localDate.format(DateTimeFormatter.ISO_LOCAL_DATE);
//自定义格式化
// DateTimeFormatter默认提供了多种格式化方式,如果默认提供的不能满足要求,可以通过DateTimeFormatter的ofPattern方法创建自定义格式化方式
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(“dd/MM/yyyy”);
String s3 = localDate.format(dateTimeFormatter);
解析时间
LocalDate localDate1 = LocalDate.parse(“20190910”, DateTimeFormatter.BASIC_ISO_DATE);
LocalDate localDate2 = LocalDate.parse(“2019-09-10”, DateTimeFormatter.ISO_LOCAL_DATE);
不能单纯认为localdatetime是calender类的替代品,它和date一样是一种类型,是数据库支持的类型,并且提出更加简易的方法
8.Annotations
9.Optional类型
它是作为某个指定类型的对象的包装器或者用于那些不存在对象(null)的场景
简单来说,它是处理空值的一个更好的替代品
使用下它的三个静态方法就可以了:
public static Optional stringOptional(String input) {
return Optional.of(input);
}
简单明了——创建一个包含这个值的Optional包装器。记住——如果这个值是null的话,它会抛出NPE!public static Optional stringNullableOptional(String input) {
if (!new Random().nextBoolean()) {
input = null;
}
return Optional.ofNullable(input);
}
我个人认为是要更好一点。这样就不会有NPE的风险了——如果输入为null的话,会返回一个空的Optionalpublic static Optional emptyOptional() {
return Optional.empty();
}
如果你真的就是希望返回一个”空”值的话。“空”值并不意味着null。
好吧,那如何去消费/使用Optional呢?public static void consumingOptional() {
Optional wrapped = Optional.of(“aString”);
if (wrapped.isPresent()) {
System.out.println(“Got string - “ + wrapped.get());
}
else {
System.out.println(“Gotcha !”);
}
}
简单的方法就是检查Optional包装器是否真的有值(使用isPresent方法)——你会怀疑这和使用if(myObj != null)相比有什么好处。别担心,这个我会解释清楚的。public static void consumingNullableOptional() {
String input = null;
if (new Random().nextBoolean()) {
input = “iCanBeNull”;
}
Optional wrapped = Optional.ofNullable(input);
System.out.println(wrapped.orElse(“default”));
}
你可以使用orElse方法,这样万一封装的确实是一个null值的话可以用它来返回一个默认值——它的好处显而易见。在提取出真实值的时候可以避免调用ifPresent方法这样明显多余的方式了。public static void consumingEmptyOptional() {
String input = null;
if (new Random().nextBoolean()) {
input = “iCanBeNull”;
}
Optional wrapped = Optional.ofNullable(input);
System.out.println(wrapped.orElseGet(
() -> {
return “defaultBySupplier”;
}
));
}
这个我就有点搞不清楚了。为什么有两个同样目的的不同方法?orElse和orElseGet明明可以重载的(同名但不同参数)。
不论如何,这两个方法明显的区别就在于它们的参数——你可以选择使用lambda表达式而不是Supplier的实例来完成这个(一个函数式接口)
为什么使用Optional要比常见的null检查强?
1.使用Optional最大的好处就是可以更明白地表述你的意图——返回null值的话会让消费者感到疑惑(当真的出现NPE的时候)这是不是故意返回的,因此还得查看javadoc来进一步定位。而使用Optional就相当明了了。
2.有了Optional你就可以彻底避免NPE了——如上所提,使用Optional.ofNullable,orElse以及orElseGet可以让我们远离NPE。
Java11
2018年9月26日
安装
明确概念
1.JDK(java development kit):java开发工具包JRE+tools
是提供给java开发人员使用的,其中包含了java的开发工具,也包括了JRE
Java的开发工具:编译工具(javac.exe)和打包工具(jar.exe)
2.JRE(java runtime environment):java运行环境 JVM+class library
包括java虚拟机和java程序所需的核心类库等
3.JVM(java virture machine):java虚拟机
jvm是用C和汇编语言写的,jdk的开发工具包都是用java写的。
可以去网上查找jdk并下载安装,这里推荐使用JDK1.8
配置JDK环境变量
要使java文件写在其他文件下面,仍可以运行,就需要配置环境变量。
我的电脑→属性→高级系统设置→环境变量→系统变量:以下三个环境变量 已存在则点击“编辑”,不存在则点击“新建”
1.新建JAVA_HOME(变量名)指向的是JDK的安装路径(变量值),在此路径下能找到bin、lib等目录。
2.编辑PATH环境变量,目的是为了指向JDK的bin目录,这里面放的是各种编译执行命令。目的是使javac指令可以在任意目录下运行。
配置方法一:
将javac指令所在目录也就是JDK安装目录下的bin目录配置到path变量下,即可使javac指令在任意目录下运行。系统中本来有path环境变量,直接放后面就行,中间用分号间隔。path环境变量具有先后顺序
具体方法:
将JDK安装目录下的bin目录添加到最左边并添加分号
配置方法二(因为jdk可能有多个,主要用这个):
新建变量名为:JAVA_HOME 变量值为:JDK安装目录 将path环境变量中的JDK目录修改为:%JAVA_HOME%\bin;(%%相当于引用)
切换JDK时只需修改变量值
Tips:操作系统因不明原因导致%JAVA_HOME%环境变量失效,即使正确配置“Path”中的内容,也无法验证通过。此时可以采用“弃用JAVA_HOME”的办法,在“Path”环境变量中,直接添加JDK下bin文件夹的完整地址(即配置方法一)。
如果“弃用JAVA_HOME”之后依然无法通过验证,则要打开JDK的bin文件夹,检查此文件夹下是否有“java.exe”和“javac.exe”这两个文件,这两个可执行文件实际上就是java命令和javac命令的“本体”,如果缺失了这两个文件,JDK就无法正常使用,环境也就永远无法验证通过。
3.CLASSPATH(class文件的环境变量)一般不用配置,仅了解,不推荐配置在系统的环境变量中
创建新的变量名称:classpath 变量值设定为指定的含有class文件的目录,多个目录之间使用分号(;)分割。
作用:使classpath目录中的.class文件可以在任意目录运行
classpath目录中的配置存在先后顺序,先在第一个目录下查找,没有的话;.再继续查找第二个,依次类推
区别:path环境变量里面记录的是可执行性文件,如:.exe文件。
classpath环境变量里面记录的是java类的运行文件所在的目录如:.class文件
验证结果
cmd中输入java -version
或者输入javac
总结
这里的下载安装很简单,最主要的是JDK的环境变量配置。原理就是在path的环境变量中加入java的bin文件夹(这个bin文件夹 中会有javac.exe和java.exe),这样在运行java时,就会找到javac命令和java命令并运行。设置JAVA_HOME的目的是为了防止有多个java时,在切换版本时需要不断更改操作path,为了方便操作,当path中运行到%JAVA_HOME%.bin时,会去再访问JAVA_HOME这个变量下的java文件夹,最终目的是一样的。classpath就是一组目录的集合,它设置了一个搜索路径,当运行某个类时,会依次查找这个目录下的这个类,找到了就会运行,如果找不到就会报错。所以一般情况下,不用配置classpath,用默认的配置在当前文件下查找这个类即可。
这里的配置环境变量是接触的第一个环境变量。环境变量这个东西一通百通,以后的环境变量配置也可以参照这个原理,其根本目的都是要使系统的path路径中可以找到你的软件的bin目录。教程只是一方面,背后的原理才是以后会独立配置的关键。