Java学习笔记一Java语言概述与开发环境

语言概述

JDK的全称是Java SE Development Kit,即Java标准版开发包。它提供了编译、运行Java程序所需的各种工具和资源,包括Java编译器、Java运行时环境以及常用的Java类库等。JRE全称是Java Runtime Environment,即Java运行时环境。
在这里插入图片描述
Oracle把Java分为Java SE、Java EE和Java ME三个部分,Java SE是整个Java技术的核心和基础,是Java EE和Java ME编程的基础。Java SE和Java EE分别有相应的JDK和Java EE SDK两个开发包。如果只需学习Java SE的编程知识,则可以下载标准的JDK;如果还需进一步学习Java EE相关内容,可以选择下载Java EE SDK,有一个Java EE SDK版本里已经包含了最新版的JDK,安装Java EE SDK就包含了JDK。

Java程序源文件的命名不是随意的,Java文件的命名必须满足如下规则:

  • Java程序源文件的扩展名必须是.java,不能是其他文件扩展名
  • 在通常情况下,Java程序源文件的主文件名可以是任意的。但有一种情况例外:如果Java程序源代码里定义了一个public类,则该源文件的主文件必须与该public类的类名相同。–>一个Java源文件可以包含多个类定义,但最多只能包含一个public类定义,则该源文件的文件名必须与这个public类的类名相同。

JDK 9工具的改进就是提供jshell工具,它是一个REPL(Read-Eval-Print Loop)工具,该工具是一个交互式的命令行界面,可用于执行Java语言的变量声明、语句和表达式,而且可以立即看到执行结果。因此,可以使用该工具快速学习Java或测试Java的新API。

对象是Java程序的核心,所以Java里的对象具有唯一性,每个对象都有一个标识来引用它,如果某个对象失去了标识,这个对象将变成垃圾,只能等着系统垃圾回收机制来回收它。Java语言不允许直接访问对象,而是通过对对象的引用来操作对象。

数据类型分类

Java语言支持的类型分为两类:基本类型(Primitive Type)和引用类型(Reference Type)。引用类型包括接口数组类型,还有一个特殊的null类型。实际上引用类型变量就是一个指针,只是Java语言里不再使用指针这个说法了。

基本类型

Java的基本数据类型分为两大类:布尔类型、数值类型。数值类型又分为整数类型、浮点类型和字符类型。

整型

通常情况下,直接给出一个整数值默认就是int类型。如果直接使用一个巨大的整数值(超过int类型的表数范围)时,Java也不会自动把这个整数值当成long类型来处理。应该在这个整数值后增加l或L作为后缀。如果直接将一个较小的整数值(在byte或short类型的表数范围内)赋给一个byte或short变量,系统会自动把这个整数值当成byte或short类型来处理。将较小的整数值(在int类型的表数范围内)直接赋给一个long类型的变量,Java不会把这个较小的整数值当成long类型来处理,而是把它当作int类型来处理,但是int类型值会自动类型转换到long类型。
Java中整数值有4种表示方式:十进制、二进制、八进制和十六进制。其中二进制的整数以0b或0B开头。

字符型

Java语言使用16位的Unicode字符集作为编码方式。char类型的值可直接作为整型值来使用,它相当于一个16位的无符号整数,表数范围是0-65535。

浮点型

只有浮点类型的数值才可以使用科学计数法形式表示。Java提供了三个特殊的浮点数值:正无穷大、负无穷大和非数,用于表示溢出和出错。例如,使用一个整数除以0将得到正无穷大,使用一个负数处以0将得到负无穷大,0.0除以0.0或对一个负数开方将得到一个非数。正无穷大通过Double或Float类的POSITIVE_INFINITY表示;负无穷大通过Double或Float类的NEGATIVE_INFINITY表示,非数通过Double或Float类的NaN表示。所有的正无穷大数值都是相等的,所有的负无穷大数值都是相等的;而NaN不与任何数值相等,甚至和NaN都不相等。只有浮点数除以0才可以得到正无穷大或负无穷大(Java语言将0(整数)当作0.0(浮点数)处理)。而整数除0,则会抛出ArithmeticException:/by zero异常。

布尔型

boolean类型的数值只能是true或false,不能用0或非0来代表。其他基本数据类型的值也不能转换成boolean类型。—>避免了C++中很多坑
如果使用一个boolean类型的值和字符串进行连接运算,则boolean类型的值将自动转换成字符串(“true”或"false")。

基本类型的包装类

对于数值类型的基本类型的取值范围,我们无需强制去记忆,因为它们的值都已经以常量的形式定义在对应的包装类中了。

public class PrimitiveTypeTest {  
    public static void main(String[] args) {  
        // byte  
        System.out.println("基本类型:byte 二进制位数:" + Byte.SIZE);  
        System.out.println("包装类:java.lang.Byte");  
        System.out.println("最小值:Byte.MIN_VALUE=" + Byte.MIN_VALUE);  
        System.out.println("最大值:Byte.MAX_VALUE=" + Byte.MAX_VALUE);  
        System.out.println();  
  
        // short  
        System.out.println("基本类型:short 二进制位数:" + Short.SIZE);  
        System.out.println("包装类:java.lang.Short");  
        System.out.println("最小值:Short.MIN_VALUE=" + Short.MIN_VALUE);  
        System.out.println("最大值:Short.MAX_VALUE=" + Short.MAX_VALUE);  
        System.out.println();  
  
        // int  
        System.out.println("基本类型:int 二进制位数:" + Integer.SIZE);  
        System.out.println("包装类:java.lang.Integer");  
        System.out.println("最小值:Integer.MIN_VALUE=" + Integer.MIN_VALUE);  
        System.out.println("最大值:Integer.MAX_VALUE=" + Integer.MAX_VALUE);  
        System.out.println();  
  
        // long  
        System.out.println("基本类型:long 二进制位数:" + Long.SIZE);  
        System.out.println("包装类:java.lang.Long");  
        System.out.println("最小值:Long.MIN_VALUE=" + Long.MIN_VALUE);  
        System.out.println("最大值:Long.MAX_VALUE=" + Long.MAX_VALUE);  
        System.out.println();  
  
        // float  
        System.out.println("基本类型:float 二进制位数:" + Float.SIZE);  
        System.out.println("包装类:java.lang.Float");  
        System.out.println("最小值:Float.MIN_VALUE=" + Float.MIN_VALUE);  
        System.out.println("最大值:Float.MAX_VALUE=" + Float.MAX_VALUE);  
        System.out.println();  
  
        // double  
        System.out.println("基本类型:double 二进制位数:" + Double.SIZE);  
        System.out.println("包装类:java.lang.Double");  
        System.out.println("最小值:Double.MIN_VALUE=" + Double.MIN_VALUE);  
        System.out.println("最大值:Double.MAX_VALUE=" + Double.MAX_VALUE);  
        System.out.println();  
  
        // char  
        System.out.println("基本类型:char 二进制位数:" + Character.SIZE);  
        System.out.println("包装类:java.lang.Character");  
        // 以数值形式而不是字符形式将Character.MIN_VALUE输出到控制台  
        System.out.println("最小值:Character.MIN_VALUE="  
                + (int) Character.MIN_VALUE);  
        // 以数值形式而不是字符形式将Character.MAX_VALUE输出到控制台  
        System.out.println("最大值:Character.MAX_VALUE="  
                + (int) Character.MAX_VALUE);  
    }  
}

在这里插入图片描述
在JDK 1.5以前,手动装箱和拆箱:基本类型变量 --通过WarpperClass.valueOf()方法创建--> 包装类对象 -- 通过WrapperInstance.xxxValue()方法--> 基本类型变量
JDK 1.5提供了自动装箱和自动拆箱功能。可以把基本类型变量直接赋给对应的包装类变量,或者赋给Object对象(包装类的父类变量);直接把包装类对象直接赋给一个对应的基本类型变量。

Integer inObj = 5; // 直接把一个基本类型变量赋给Integer对象
Object boolObj = true;  // 直接把一个布尔类型变量赋给Object对象
int it = inObj; // 直接把一个Integer对象赋给基本类型变量
if (boolObj instanceof Boolean)
{
	boolean b = (Boolean)boolObj;  // 强制类型转换后赋值
}

基本类型和字符串之间的转换:基本类型 --> 通过String.valueOf(primitive)或基本类型+""转换 --> String对象 --> 通过WrapperClass.parseXxx()方法或valueOf()方法 --> 基本类型

包装类的实例可以与数值类型的值进行比较,直接取出包装类实例所包装的数值进行比较。两个包装类的实例进行比较的情况比较复杂(包装类的实例是引用类型),只有两个包装类引用指向同一个对象时才会返回true。

Integer a = Integer.valueOf(6);
System.out.println("6的包装类实例是否大于5.0:"+(a > 5.0)); // true
System.out.println("比较2个包装类的实例是否相等:"+(Integer.valueOf(2) == Integer.valueOf(2))); // false

两个自动装箱的包装类变量是否相等

Integer ina = 2;
Integer inb = 2;
System.out.println("两个2自动装箱后是否相等:"+(ina == inb)); // true
Integer biga = 128;
Integer bigb = 128;
System.out.println("两个128自动装箱后是否相等:"+(biga == bigb)); // false

系统将-128-127之间的数创建成Integer类,提供给用户程序共享(也就是下面名为cache的数组)。如果把一个-128-127之间的整数自动装箱成一个Integer实例时,实际上是直接指向对应的数组元素,因此都是引用cache数组中的同一个数组元素。否则,系统会重新创建一个Integer实例。这样的做法并不少见,在之前查看密码学C/C++语言实现的部分源码中以及Redis中的比较小的整数的实现也是这种情况。

// 定义一个长度为256的Integer数组
static final Integer[] cache = new Integer[-(-128) + 127 + 1];
static {
	// 执行初始化,创建-128到127的Integer实例,并放入cache数组中
	for(int i = 0; i < cache.length; i++)
	{
		cache[i] = new Integer(i-128);
	}
}

通过new构造器创建Integer对象不会启用缓存,具体解释

public class IntegerTest{
	Interger in1 = new Integer(6);
	Interger in2 = Integer.valueOf(6);
	Interger in3 = Integer.valueOf(6);
	System.out.println(in1 == in2); // false
	System.out.println(in2 == in3); // true
}

为何需要包装类?
OO语言都希望能“万物皆对象”,并以此为基础来设计整个语言。比如会提供基类Object,以及基于这个基类的类型系统;有了Object,就能统一对内存的管理——在托管堆上管理,并且实现GC;可以支持多态的方法调用。比如写foo.doSomethinig()这样的代码时,是要去根据foo的类型去查找真正要调用的doSomethinig到底是哪个;顺着这个思路,可以在基类Object中塞一些非常基础的方法(toString、equals等),并让上层可以“直接复用”或者“覆写“。
但现实中的大量代码不需要像对象那样在托管堆上分配内存,不需要GC,不需要“方法”。他们仅仅需要在栈上快速的分配空间,利用最基本的运算符进行运算、比较和赋值。【对象】对于这类代码显得非常多余且低效。想象一下要对一堆整数进行排序,直接分配一段内存,再用快排这类算法直接“过一遍“内存上的值就可以了。这远比整出一堆【对象】再通过其引用折腾来折腾去,再GC掉好得多。
这就引入了一个两难的问题,一方面期望用“万物皆对象”的方式来统一语言设计,但另一方面又不得不考虑对“值类型”数据提供支持。
对于这个问题,一般都采用boxing/unboxing的方式实现。即对于每一种数据类型(primitive type),提供一个对应的引用类型(reference type)。把值类型转换成引用类型对象,相当于创建一个“Wraper”对象,被称为装箱(boxing)。反过来,把一个引用类型里的值取出来,被称为拆箱 (unboxing)。

Integer a = new Integer(1); // 装箱
int b = a.intValue(); // 拆箱

boxing/unboxing是反直觉的。本来程序员考虑1、true,2.35这样的值就可以了,但就因为其内存模型不一样,不得不考虑到底要用值还是要用对象。Java 1.4之前,boxing/unboxing是必须手工做的。也就是程序员必须手写上面的代码,不胜其烦。Java 1.5终于提供了“auto” boxing/unboxing,即让编译器自动帮助插入boxing/unboxing代码。这样就可以像下面这样写代码了。

Character c = ‘c’; // 'c’这个值被自动装箱,并把变量c赋值为装箱后的对象的引用。
List list = new ArrayList();
list.add(1); // 1 这个值被自动装箱,并把装箱好的对象引用塞给list
int v = list.get(0); // list里的第0个对象被取出,自动拆箱,把值赋给v

这的确避免了很多繁琐的boxing/unboxing代码。但到目前为止Java里始终不能彻底让编译器完全介入boxing/unboxing:

1.toString(); // Java里编译错误

这个代码在C#和javascript里都是合法的。原理很简单,只要把1直接装箱成一个对象,然后在上面调用方法就行了。基于JVM的kotlin和groovy也都支持这么干。Java的另外一个问题是始终无法对primitive type做真正的collection。Java里提供的collection里只能存引用类型。这会让一些场景很不方便。C#早期和Java一样,但随后提供了“真范型“并重新提供了一套新的Collection,彻底解决了这个问题。Java始终没有做这件事。顺便提一句C#提供了值类型struct,可以让程序员直接管理数据的内存布局,甚至还可以“pin”住一个对象在内存中的位置,避免其因为GC导致内存位置变化。这些Java都没有提供。因此Java始终也无法满足系统编程领域的需求。
最好的boxing/unboxing就是让程序员感受不到它们的存在,并自动产生最有效率,最低代价的代码。而Java明显没做到这点,只能捏着鼻子用了。
扩展下,boxing/unboxing本质上是由于“万物皆对象”落地时带来的一个有点恶,但不得不面对和解决的问题。但如果,不再要求“万物皆对象”,是否可以呢?以 1.toString(); 这个代码为例子。假如不从OO角度出发,仅仅是作为一种函数的语法糖。当编译器碰到这段代码,就直接去找一个toString(int self);的函数是否可以呢?进一步的,提供:toString(char self); toString(boolean self); toString(float self);满足全部built-in的值类型的需要,是否可以呢?

基本类型的类型转换

把任何基本类型的值和字符串进行连接运算时,基本类型的值将自动类型转换为字符串类型,比如String str = 3.5f + "";。把一个浮点数强制类型转换为整数时,Java将直接截断浮点数的小数部分。在通常情况下,字符串不能直接转换为基本类型,通过包装类则可以实现把字符串转换成基本类型。

String a = "45";
// 使用Integer方法将一个字符串转换成int类型
int iValue = Integer.parseInt(a);

常量

常量是指在java程序运行期间固定不变的数据
在 Java 中使用 final 关键字来修饰常量,声明方式和变量类似:final double PI = 3.1415927;

有三种类型能指定直接量(字面常量):基本类型、字符串类型和null类型。

在这里插入图片描述
当程序第一次使用某个字符串直接量时,Java会使用常量池(constant pool)来缓存该字符串直接量,如果程序后面的部分需要用到该字符串直接量时,Java会直接使用常量池中的字符串直接量。由于String类是一个典型的不可变类,因此String对象创建出来就不可改变

String s0 = "hello";
String s1 = "hello";
String s2 = "he"+"llo";
System.out.println(s0 == s1); //true
System.out.println(s0 == s2); //true

Java会确保每个字符串常量只有一个,不会产生多个副本。s0和s1中的“hello”都是字符串常量,他们在编译期就被确定了,所以s0 == s1返回true;而"he"和"llo"也是字符串常量,当一个字符串由多个字符串常量连接而成时,它本身也是字符串常量,s2同样在编译期就被解析为一个字符串常量,所以s2也是常量池中"hello"的引用。

运算符

求余运算符需要除法运算,如果两个操作数都为整数类型且第二个操作数为0,将引发除以零异常。如果求余运算的两个操作数中有一个或者两个都是浮点数,则允许第二个操作数是0或0.0,只是求余运算的结果是非数NaN。0或0.0对零以外的任何数求余都将得到0或0.0。

对于低于int类型(如byte、short和char)的操作数总是先自动类型转换为Int类型后再移位。
对于int类型的整数移位a>>b,当b>32时,系统先用b对32求余,得到的结果才是真正移位的位数。例如a>>33和a>>1的结果完全一样。对于long类型的依次类推。

基本类型的变量、值不能和引用类型的变量、值使用==进行比较;boolean类型的变量、值不能与其他任意类型的变量、值使用==进行比较;如果两个引用类型之间没有父子继承关系,那么他们的变量也不能使用==进行比较。

&:不短路与,前后两个操作数都是true才返回true,不会短路。
|:不短路或,只要两个操作数中有一个是true,就可以返回true,不会短路。
^:异或,当两个操作数不同时才返回true。

结果 操作数1:true 操作数2:true 操作数1:true 操作数2:false 操作数1:false 操作数2:true 操作数1:false 操作数2:false
&& 计算1和2 返回true 计算1和2 返回false 计算1 不计算2 返回false 短路 计算1 不计算2 返回false 短路
& 计算1和2 返回true 计算1和2 返回false 计算1和2 返回false 计算1和2 返回false
|| 计算1 不计算2 返回true 短路 计算1 不计算2 返回true 短路 计算1和2 返回true 计算1和2 返回false
| 计算1和2 返回true 计算1和2 返回true 计算1和2 返回true 计算1和2 返回false

单目运算符、赋值运算符和三目运算符是从右向左结合的。

流程控制

使用if…else语句时,一定要先处理包含范围更小的情况。

switch语句后面的控制表达式的数据类型只能是byte、short、char、int四种整数类型枚举类型和 java.lang.String类型(从Java7起),不能是boolean类型。

for循环和while、do while循环不一样:由于while、do while循环迭代语句紧跟着循环体,因此如果循环体不能完全执行,如使用continue语句来结束本次循环,则循环迭代语句不会被执行。但for循环迭代语句并没有与循环体放在一起,因此不管是否使用continue语句来结束本次循环,循环迭代语句一样会获得执行。

break语句不仅可以结束其所在的循环,还可以直接结束其外层循环。此时需要在break后紧跟一个标签,这个标签用于标识一个外层循环。Java中的标签就是一个紧跟着英文冒号的标识符。与其他语言不同的是,Java中的标签只有放在循环语句之前才有作用。与break类似,continue后也可以紧跟一个标签,用于直接跳过标签所标识循环的当次循环剩下语句,重新开始下一次循环。

public static void main(String[] args)
{
	// 外层循环, outer作为标识符
	outer:
	for (int i = 0; i < 5; i++)
	{
		for (int j = 0; j < 3; j++)
		{
			System.out.println("i的值为:"+i+" j的值为:"+j);
			if(j==1) //跳出outer标签所标识的循环
				break outer;
		}
	}
}

开发环境

设置环境路径
在这里插入图片描述在这里插入图片描述
测试:https://blog.csdn.net/Hhhana/article/details/82562687?utm_source=blogkpcl5

发布了134 篇原创文章 · 获赞 141 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/asmartkiller/article/details/104711424