Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征。Java语言作为静态面向对象编程语言的代表,极好地实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的编程 [1] 。
Java具有简单性、面向对象、分布式、健壮性、安全性、平台独立与可移植性、多线程、动态性等特点 [2] 。Java可以编写桌面应用程序、Web应用程序、分布式系统和嵌入式系统应用程序等 [3] 。
Oracle官方编写了部分Java教程文档,可参考:Trail: Learning the Java Language: Table of Content (The Java™ Tutorials) (oracle.com)
Java基础
Java基础,包含了入门程序、基础语法、流程控制、数组等
第一个Java程序
从java的环境搭建,到编写一个简单的java程序过程。
windows安装Jdk
jDK是 Java 语言的软件开发工具包,主要用于移动设备、嵌入式设备上的java应用程序。JDK是整个java开发的核心,它包含了JAVA的运行环境(JVM+Java系统类库)和JAVA工具。
jdk在oracle官网下载,以下是本站提供的一些jdk
- jdk-9.0.1_windows-x64_bin.exe
- jdk-8u161-windows-x64.exe
- jdk-7u17-windows-x64.exe
- jdk-6u45-windows-x64.exe
下载jdk安装程序,例如jdk9,双击开始安装
通常来说,我们不需要公共jre,因为开发工具jdk中已经包含了一个jre,在安装时不勾选公共jre
安装完毕后,通常需要配置系统环境变量,以便在任何目录下调用java
- 打开资源管理器
- 在左侧栏目中找到此电脑,右键选择“属性”
- 点击“高级系统设置”
- 点击环境变量
- 在系统变量中,点击新建,变量名为JAVA_HOME,变量值为jdk安装目录
点击确定后,在系统变量中,找到Path变量,并编辑,新增一条记录,值为%JAVA_HOME%\bin
如果你在编辑Path变量时与上述界面不同,而是一行,在已有内容中添加分号 ; 结尾,并添加%JAVA_HOME%\bin,然后点击确定即可
在添加系统变量完成后,打开cmd进行测试。输入javac命令,以及java命令,出现提示说明系统变量配置完毕。
HelloWorld程序
Java被称为“半编译、半解释”程序,以下将体验这个过程:
编写一个HelloWorld.java文件,并写如下代码
import java.util.Scanner;
public class HelloWorld {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入姓名:");
String name = sc.next();
System.out.println("你好,"+name+",您正在使用Java程序。");
}
}
打开cmd,进入java文件所在位置,并执行javac命令
javac HelloWorld.java
此时,会生成一个HelloWorld.class文件,我们执行java命令即可运行该字节码文件
java HelloWorld
在这个过程中,javac将源代码编译为字节码文件(HelloWorld.class),最后由java虚拟机解释字节码文件。
- 从编写角度来看,java是编译型的,它和其他编译语言一样繁琐
- 从变量类型来看,java是静态语言,它和其它编译语言一样麻烦,但清晰
- 从执行角度来看,java是解释型的,它和其他解释型语言一样低性能
综上,java的编写、运行是麻烦的,性能方面低于编译型语言,又高于解释性语言。
main函数
主函数(main)是一个程序的入口点,主函数的格式是固定的
public static void main(String[] args){
方法体;
}
- 必须是被 public 修饰的
- 必须是被 static 修饰的
- 返回值必须是 void
- 函数名必须是 main
- 参数必须是一个字符串数组 String[]
在一个java文件中,只能有一个main方法。
变量及其使用
变量:是一个存储数据的容器,其中的数据可以在程序运行时根据需要随时改变;
位置:变量是在内存在内存当中的,其实就是内存的一小块单元区域
变量的定义
语法: 数据类型 变量名 = 初始化值
/*声明一个变量:
语法: 数据类型 变量名 = 初始化值
解读: 定义一个变量 num ,并将20赋值给num
*/
int num = 20;
/*------------------------------*/
/**多个变量的定义方式:
* 可以同时定义多个变量
*/
//声明一个变量并赋值
int num2 = 20;
//先声明一个变量,后赋值
int num3;
num3 = 20;
//声明多个变量,在赋值
int a , b , c;
a = 10;
b = 20;
c = 30;
//声明多个变量,同时赋值
int d = 10, e = 20, f = 30;
//声明多个变量,直接赋值与后赋值
int g , h = 10, m = 20;
g = 100;
变量输出
使用System.out,println输出变量。
在程序运行过程中,变量的值可以重新赋值
/**
* 变量在运行的时候随时可以改变:
*/
//赋初值
int p1 = 20;
System.out.println("p1的值为:" + p1);//20
//重新赋值
p1 = 200;
System.out.println("p1的值为:" + p1);//200
//变量参与运算
int p2 = 300;
p2 = p2 + p1;
System.out.println("p2的值为:" + p1);
注意:
- 变量的数据类型必须和值的类型匹配
- 变量不能重名
- 变量未赋初值,不能使用
int v1 = "你好"; // 编译报错 ,数据类型不匹配
int v2 = 20;
int v2 = 30;// 编译报错,变量不能重名
int v3;
System.out.println(v3);// 编译报错,半两为赋值
命名规则
1 由 英文 数字 下划线 $ 组成
2 不能以数字开头
3 严格区分大写小
4 不能使用关键字与保留字
合法标识符:
vince、daoguo、user_name、_userName、$abc_123
非法标识符:
2UserName、user#Name、Hello谭老师、class
建议:见名知意
- 一个单词:单词的首字母小写
- 多个单词:首字母小写,从第二个单词开始 每一个单词首字母都要大写
int 3 = 10; // 编译报错
int num_2 = 30; //正确
int NUM_2 = 40; //正确
// int class = 30; //编译报错,不能使用关键字
/**
* 见名知意思:
*/
int count = 10;
int countStudent = 27;//驼峰命名法
此外,可以小写下划线也是一种很好的命名法,例如 user_name。
注释
- 单行注释 // 【ctrl +/】
- 多行注释 /* */ 【ctrl+shift+/】
第三种注释是:文档注释
文档注释,通常是介绍 class ,或 方法 的,做接口文档
可以用专门的工具,作快速导出生成接口文档,给其他人员查阅
/**
该注释,语法和多行注释相似,开始符号多一个 *
*/
数据类型
数据类型分为基本数据类型和引用数据类型,基本数据类型有以下8种
- byte/8
- char/16
- short/16
- int/32
- float/32
- long/64
- double/64
- boolean/~
boolean 只有两个值:true、false,可以使用 1 bit 来存储,但是具体大小没有明确规定。JVM 会在编
译时期将 boolean 类型的数据转换为 int,使用 1 来表示 true,0 表示 false。JVM 支持 boolean 数
组,但是是通过读写 byte 数组来实现的。
基本类型都有对应的包装类型,基本类型与其对应的包装类型之间的赋值使用自动装箱与拆箱完成。
Integer x = 2; // 装箱 调用了 Integer.valueOf(2)
int y = x; // 拆箱 调用了 X.intValue()
在 Java 8 中,Integer 常量池的大小默认为 -128~127。
new Integer(123) 与 Integer.valueOf(123) 的区别在于:
- new Integer(123) 每次都会新建一个对象;
- Integer.valueOf(123) 会使用缓存池中的对象,多次调用会取得同一个对象的引用。
Integer x = new Integer(123);
Integer y = new Integer(123);
System.out.println(x == y); // false
Integer z = Integer.valueOf(123);
Integer k = Integer.valueOf(123);
System.out.println(z == k); // true
它的底层源码如下:
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
其他基本类型的常量池如下:
- boolean values true and false
- all byte values
- short values between -128 and 127
- int values between -128 and 127
- char in the range \u0000 to \u007F
在 jdk 1.8 所有的数值类缓冲池中,Integer 的缓冲池 IntegerCache 很特殊,这个缓冲池的下界是 – 128,上界默认是 127,但是这个上界是可调的,在启动 jvm 的时候,通过 -XX:AutoBoxCacheMax= 来指定这个缓冲池的大小,该选项在 JVM 初始化的时候会设定一个名为 java.lang.IntegerCache.high 系统属性,然后 IntegerCache 初始化的时候就会读取该系统属性来决定 上界。
String
String 被声明为 final,因此它不可被继承。(Integer 等包装类也不能被继承)
在 Java 8 中,String 内部使用 char 数组存储数据。
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
}
在 Java 9 之后,String 类的实现改用 byte 数组存储字符串,同时使用 coder 来标识使用了哪种编码。
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final byte[] value;
/** The identifier of the encoding used to encode the bytes in {@code value}.
*/
private final byte coder;
}
value 数组被声明为 final,这意味着 value 数组初始化之后就不能再引用其它数组。并且 String 内部没
有改变 value 数组的方法,因此可以保证 String 不可变。
不可变的好处
可以缓存 hash 值
因为 String 的 hash 值经常被使用,例如 String 用做 HashMap 的 key。不可变的特性可以使得 hash
值也不可变,因此只需要进行一次计算
如果一个 String 对象已经被创建过了,那么就会从 String Pool 中取得引用。只有 String 是不可变的,
才可能使用 String Pool。
安全性
String 经常作为参数,String 不可变性可以保证参数不可变。例如在作为网络连接参数的情况下如果
String 是可变的,那么在网络连接过程中,String 被改变,改变 String 的那一方以为现在连接的是其它
主机,而实际情况却不一定是。
线程安全
String 不可变性天生具备线程安全,可以在多个线程中安全地使用。
String, StringBuffer and StringBuilder
可变性
- String 不可变
- StringBuffer 和 StringBuilder 可变
线程安全
- String 不可变,因此是线程安全的
- StringBuilder 不是线程安全的
- StringBuffer 是线程安全的,内部使用 synchronized 进行同步
String Pool
字符串常量池(String Pool)保存着所有字符串字面量(literal strings),这些字面量在编译时期就确
定。不仅如此,还可以使用 String 的 intern() 方法在运行过程将字符串添加到 String Pool 中。
当一个字符串调用 intern() 方法时,如果 String Pool 中已经存在一个字符串和该字符串值相等(使用
equals() 方法进行确定),那么就会返回 String Pool 中字符串的引用;否则,就会在 String Pool 中添
加一个新的字符串,并返回这个新字符串的引用。
下面示例中,s1 和 s2 采用 new String() 的方式新建了两个不同字符串,而 s3 和 s4 是通过 s1.intern()
和 s2.intern() 方法取得同一个字符串引用。intern() 首先把 “aaa” 放到 String Pool 中,然后返回这个字
符串引用,因此 s3 和 s4 引用的是同一个字符串。
String s1 = new String("aaa");
String s2 = new String("aaa");
System.out.println(s1 == s2); // false
String s3 = s1.intern();
String s4 = s2.intern();
System.out.println(s3 == s4); // true
如果是采用 “bbb” 这种字面量的形式创建字符串,会自动地将字符串放入 String Pool 中。
String s5 = "bbb";
String s6 = "bbb";
System.out.println(s5 == s6); // true
在 Java 7 之前,String Pool 被放在运行时常量池中,它属于永久代。而在 Java 7,String Pool 被移到
堆中。这是因为永久代的空间有限,在大量使用字符串的场景下会导致 OutOfMemoryError 错误。
参数传递
Java 的参数是以值传递的形式传入方法中,而不是引用传递。
以下代码中 Dog dog 的 dog 是一个指针,存储的是对象的地址。在将一个参数传入一个方法时,本质
上是将对象的地址以值的方式传递到形参中。
public class Dog {
String name;
Dog(String name) {
this.name = name;
}
String getName() {
return this.name;
}
void setName(String name) {
this.name = name;
}
String getObjectAddress() {
return super.toString();
}
}
在方法中改变对象的字段值会改变原对象该字段值,因为引用的是同一个对象。
class PassByValueExample {
public static void main(String[] args) {
Dog dog = new Dog("A");
func(dog);
System.out.println(dog.getName()); // B
}
private static void func(Dog dog) {
dog.setName("B");
}
}
float 与 double
Java 不能隐式执行向下转型,因为这会使得精度降低。
1.1 字面量属于 double 类型,不能直接将 1.1 直接赋值给 float 变量,因为这是向下转型。
// float f = 1.1;
1.1f 字面量才是 float 类型。
float f = 1.1f;
隐式类型转换
因为字面量 1 是 int 类型,它比 short 类型精度要高,因此不能隐式地将 int 类型向下转型为 short 类
型。
short s1 = 1;
// s1 = s1 + 1;
但是使用 += 或者 ++ 运算符会执行隐式类型转换。(与变量进行四则运算时自动提升为int)
s1 += 1;
s1++;
上面的语句相当于将 s1 + 1 的计算结果进行了向下转型:
s1 = (short) (s1 + 1);
waiting…
本篇完,还有疑问?留下评论吧