Java基础语法
文章目录
一、Java概述
1.1 什么是Java
Java是一门面向对象的编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征。Java语言作为静态面向对象编程语言的代表,极好地实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的编程。
Java具有简单性、面向对象、分布式、健壮性、安全性、平台独立与可移植性、多线程、动态性等特点 。Java可以编写桌面应用程序、Web应用程序、分布式系统和嵌入式系统应用程序等。
1.2 Java语言的发展历史
1.3 Java语言平台
Java语言分为三大类,不同的类型针对不同的开发场景。
J2SE(标准版)
该版本是其它两者的基础,主要应用于普通桌面应用的开发。
J2ME(小型版)
该版本是Java的微型版,主要应用于嵌入式系统的开发,如手机系统、App的开发。
J2EE(企业版)
主要应用于开发企业级的应用,例如web应用程序的开发。
1.4 JDK、JRE和JVM
JDK(Java开发工具包)
JDK包含了JRE和JVM,并且包含了java程序的编译工具(javac.exe)。它提供了大量的API可以供Java程序员使用。
JRE(Java运行环境)
JRE提供了Java程序的运行环境,它包含了JVM。
JVM(Java虚拟机)
用于解释运行Java的字节码文件。也是Java语言实现跨平台的依赖。
JVM为什么能实现跨平台
不同平台的JVM会通过解释器将字节码文件编译成能够在对应的操作系统上运行的机器码文件。所以也可以说我们是在面向JVM编程。
二、程序的编译过程
实例:创建一个输出hello world的java程序
public class HelloWorld{
public static void main(String[] args){
System.out.println("hello world");
}
}
因为java 是面向对象的编程语言,所以我们实际上是在创建一个个的类
class HelloWorld 表示这个类的名字是HelloWorld
public static void main(String[] args) 这是主方法,所有代码的入口
执行过程
// 1. 将Java源文件编译成字节码文件
javac HelloWorld.java
// 2. JVM运行编译后的字节码文件
java HelloWorld
源文件由开发人员编写,然后JDK中的编译器(javac.exe)会将源文件编译成字节码文件,然后JVM会通过解释器去解释运行字节码文件。
.java 文件是java的源文件,但是不能直接运行,必须先被编译成为.class文件 才能够执行
java 使用 javac命令进行编译
三、类的加载
2.1 类的加载过程
Java代码分为三个阶段
- 源代码阶段
- class类对象阶段
- 运行时阶段
2.2 类加载器
类加载器的种类
- AppClassLoader 主要是帮助我们加载我们自己写的代码或者class文件
- ExtClassLoader 主要是加载E:\develop\Java\jdk1.8.0_192\jre\lib\ext下面的jar包或者class文件
- BootStrapClassLoader 主要是E:\develop\Java\jdk1.8.0_192\bin下面的jar包或者class文件
双亲委派
AppClassLoader的父类加载器是ExtClassLoader,ExtClassLoader的父类加载器是BootStrapClassLoader。
JVM在加载一个类的时候,会调用AppClassLoader的loadClass方法来加载这个类,不过在这个方法中,会先使用ExtClassLoader的loadClass方法来加载类,同样ExtClassLoader会先使用BootStrapClassLoader来加载类,如果BootStrapClassLoader加载到了就直接成功,如果没有加载到,那么ExtClassLoader就会自己加载,如果ExtClassLoader也没加载到,再由AppClassLoader自己加载这个类。
所以,双亲委派就是,JVM在加载类时,会先委派给ExtClassLoader 和BootStrapClassLoader 加载,如果没有加载到才由AppClassLoader 自己加载。
扩展
相同的类加载器加载相同的类,内存中只会有一个class对象
不同的类加载器加载相同的类,内存中会有多个class对象
例如,热部署
热部署的原理就是通过监听器监听代码是否被改动,如果被改动,就由不同的类加载器加载出另一个类对象。这样就实现了不用重启服务器并且整改了系统。
四、Java的基本数据类型
4.1 罗列基本数据类型
四类 | 八种 | 字节数 | 数据范围 |
---|---|---|---|
字节 | byte | 1 | -128 ~ 127 |
短整型 | short | 2 | -32768 ~ 32767 |
整型 | int | 4 | -2147483648 ~ 2147483648 |
长整型 | long | 8 | -2^63 ~ 2^63-1 |
浮点型(单精度) | float | 4 | |
浮点型(双精度) | double | 8 | |
布尔型 | boolean | 1 | 只有两个值,true或者false |
字符型 | char | 2 | 一个字符,例如’1’, ‘a’, ‘$’, ‘中’ |
注意点:
- 整型的默认数据类型是int,浮点数的默认数据类型是double
- 使用long类型的时候需要在数据后面加上L,整数的数据类型是int,如果值的范围超过int数据类型的范围,就是发生编译错误
- 使用float类型的时候需要在数据后面加上F,小数的默认数据类型是double,而double不能自动转换成float,所以会发生编译错误
- E表示10的38次方
- float的数据范围比long要大
4.2 基本数据类型转换
byte,short,char—int—long—float—double
byte,short,char相互之间不转换,他们参与运算首先转换为int类型
boolean类型不能转换为其他的数据类型
4.3 Java标识符
4.3.1 组成规则
变量命名只能使用字母 数字 $ _
变量第一个字符 只能使用 字母 $ _
变量第一个字符 不能使用数字
注:_ 是下划线,不是-减号或者—— 破折号
4.3.2 注意事项
不能以数字开头
不能是Java中的关键字
4.3.3 命名规范
见名知意
包名小写
类名每个单词的首字母大写
变量名和方法名从第二个单词开始首字母大写
五、 变量与方法
变量:用来命名一个数据的标识符
基本数据类型:存储的是一个字面值
引用类型:存储的是一个地址值
5.1 变量在不同的位置,有不同的名称
分别是
字段,属性
参数
局部变量
不同名称的变量,其作用域是不一样的
属性
当一个变量被声明在类下面
变量就叫做字段 或者属性、成员变量、Field
比如变量i,就是一个属性。
那么从第2行这个变量声明的位置开始,整个类都可以访问得到
所以其作用域就是从其声明的位置开始的整个类
参数
如果一个变量,是声明在一个方法上的,就叫做参数
参数的作用域即为该方法内的所有代码
其他方法不能访问该参数
类里面也不能访问该参数
局部变量
声明在方法内的变量,叫做局部变量
其作用域在声明开始的位置,到其所处于的块结束位置
注意:
属性与局部变量和参数互不影响,可以使用相同的变量名,在使用的过程中Java程序会采用就近原则(局部变量优先于属性);
局部变量和参数的变量名必须不同
六、Java运算符
6.1 算术运算符
运算符 | 含义 | 运算符 | 含义 |
---|---|---|---|
+ | 加 | - | 减 |
* | 乘 | / | 除(取整数部分) |
% | 取余 | ++ | 加加 |
– | 减减 |
++和–的使用注意事项
-
单独使用
没有区别
-
混合使用
-
放在变量前面
表示先加一(或减一)再使用
-
放在变量后面
表示先使用在加一(或减一)
-
-
不能与常量使用
public class Test1 {
public static void main(String[] args) {
int i = 1;
System.out.println(i++ + --i);
/*
两种结果:
1.结果为1,执行完当前这条语句之后再加一 错的
2.结果为2,使用完了之后马上加一,也就是第一个i在使用的时候为1,使用完了之后马上加一,然后再向后执行--i 对的
*/
}
}
6.2 关系运算符
6.3 逻辑运算符
运算符&:
可以做逻辑运算,也可以做位运算
逻辑运算:
//与&&用法相同
if(false & true)
位运算:
int m = 5;
int n = (m & 0b1100);
System.out.println(n);
//输出结果为4
&运算法按位模式处理,使用二进制进行计算。
m=5=0b0101;
计算方式:位数相同且同为1结果才为1
0b0101
0b1100
————
0b0100
0b0100=4,与输出结果相同
运算符&&:
逻辑与,只能用作逻辑运算
if(false && true)
结论
相同点:
都可以做逻辑运算
不同点:
- 如果在逻辑运算中,前一个表达式为结果为flase,&&则会忽略后面的表达式不去执行,而&不会忽略,还是会执行后面的表达式。
- &可以做位运算,&&不可以
七、流程控制
7.1 选择结构
7.1.1 switch case
//语句结构
switch(表达式){
case:
语句体1;
break;
case:
语句体2;
break;
......
default:
语句体n;
break;
}
switch的注意事项:
-
break的作用问题
结束case的执行 直到遇见下一个break程序才会结束
专业术语称为case穿透 有没有使用场景? 有2 .case的值顺序无关紧要
-
default后面的break可以不写 但是一般不推荐
-
switch表达式的类型可以是哪些 笔试题
jdk1.5之前 byte short int char
jdk1.5之后 还可以是枚举
jdk1.7之后 还可以是字符串
//穿透场景
public class Demo02Switch {
public static void main(String[] args) {
//根据月份判断季节
int month = 2;
switch (month){
case 2:
case 3:
case 4:
System.out.println("春季");
break;
case 5:
case 6:
case 7:
System.out.println("夏季");
break;
case 8:
case 9:
case 10:
System.out.println("秋季");
break;
case 1:
case 11:
case 12:
System.out.println("冬季");
break;
default:
System.out.println("地球没有这个季节");
//break;
}
}
}
八、常用关键字
8.1 权限修饰符
权限修饰符 | 同一个类 | 同一个包 | 不同包下的子类 | 不同包,也非子类 |
---|---|---|---|---|
public | yes | yes | yes | yes |
protected | yes | yes | yes | no |
default | yes | yes | no | no |
private | yes | no | no | no |
8.1 static关键字
方法块的执行顺序
本类的静态代码块
被调用类的静态代码块
构造代码块
构造方法
public class Test {
static {
System.out.println("Test静态代码块");
}
{
System.out.println("Test构造代码块");
}
public Test(){
System.out.println("Test无参构造方法");
}
public static void main(String[] args) {
System.out.println("main方法!");
Anaiml.name="小白";
Test t = new Test();
Anaiml a = new Anaiml();
a.toString();
class Anaiml{
static String name;
static {
System.out.println("Anaiml静态代码块!");
}
{
System.out.println("Anaiml构造代码块!");
}
public Anaiml(){
System.out.println("Anaiml无参构造方法!");
}
public Anaiml(String name){
this.name = name;
System.out.println("Anaiml有参构造方法!");
}
@Override
public String toString() {
return "Anaiml [name=" + name + "]";
}
}
/*
执行顺序:
Test静态代码块
Anaiml静态代码块!
Test构造代码块
Test无参构造方法
Anaiml构造代码块!
Anaiml无参构造方法!
Anaiml构造代码块!
Anaiml有参构造方法!
*/
注意
static修饰的变量会在加载类的时候第一时间定义,并且会给默认值
然后按照代码顺序执行
public class SingleTon {
private static SingleTon singleTon = new SingleTon();
public static int count1=2;
public static int count2 = 0;
private SingleTon() {
System.out.println(count1);//0
count1++;
count2++;
}
public static SingleTon getInstance() {
return singleTon;
// return new SingleTon();
}
}
class Test{
public static void main(String[] args) {
SingleTon singleTon = SingleTon.getInstance();
}
}
九、数组
9.1 数组的概述
数组是存储多个变量(元素)的东西(容器)
这多个变量的数据类型要一致
数组是引用类型
数组可以解决重复定义同类型变量的冗余问题。例如可以存放班级学生的成绩
9.2 数组的定义
9.2.1 动态定义
//数据类型[] 变量名 = new 数据类型[数组长度];
//1.
int[] arr = new int[10];
//2.
int[] arr2 = null;
arr2 = new int[10];
9.2.2 静态定义
//1. 数据类型[] 变量名 = new 数据类型[]{元素,元素,...,元素};
int[] arr = new int[]{1,2,3};
//2. 数据类型[] 变量名 = {元素,元素,...,元素};
int[] arr2 = {1,2,3};
9.3数组的内存图解
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DLhrJVoe-1662125292069)(E:\develop\学习记录\day04\1.一个数组的内存图.png)]
创建一个数组,先在堆里面划分一块存放数组长度的区域
然后在栈里面创建数组变量,并将堆里面的数组地址给到这个变量
调用这个变量名时就是通过这个地址找到堆里面的数组
arr[0]变量名后面加上数字就是先通过地址找到堆里面的数组,然后通过后面的数字索引找到数组里面的元素
十、String(字符串)
10.1 创建方式以及构造方法
10.2 String变量的内存区域
常量池:存放共享的常量
1.7及以前存放在方法区里,1.8及以后存放在堆里面
jdk1.7及以前:老年代、新生代、永久代(方法区)
jdk1.8及以后:老年代、新生代、(元空间)
10.3 区别
String str1 = "hello";
String str2 = new String("hello");
str1是直接引用的"hello"这个常量的变量
str2是先创建了一个String类的对象,然后由对象去引用的"hello"这个常量。
常量是共享的,所以引用了相同常量的对象地址也想等
10.4 常用方法
10.4.1String类的判断功能
equals
功能:判断字符串的内容与参数obj是否相同
用例
//boolean equals(Object obj)
public class Test02String {
public static void main(String[] args) {
String str1 = "hello";
System.out.println(str1.equals("hello"));//true
System.out.println("hello".equals(str1));//true
str1 = null;
System.out.println(str1.equals("hello"));//出现空指针异常
System.out.println("hello".equals(str1));//false
}
}
源码
public boolean equals(Object anObject) {
//判断内存地址,如果相等,直接返回true
if (this == anObject) {
return true;
}
//instanceof:用来测试一个对象是否为一个类的实例
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
equalsIgnoreCase
功能:判断字符串的内容是否相同(不区分大小写)
用例
//boolean equalsIgnoreCase(String str)
public class Test02String {
public static void main(String[] args) {
String str1 = "hello";
System.out.println(str1.equalsIgnoreCase("HeLLo"));//true
System.out.println("HeLLo".equalsIgnoreCase(str1));//true
}
}
startsWith
功能:判断字符串对象是否是参数str开头
用例
//boolean startsWith(String str)
public class Test02String {
public static void main(String[] args) {
String str1 = "hello";
System.out.println(str1.startsWith("h"));//true
System.out.println(str1.startsWith("hel"));//true
}
}
endsWith
功能:判断字符串对象是否是参数str结尾
//boolean endsWith(String str)
public class Test02String {
public static void main(String[] args) {
String str1 = "hello";
System.out.println(str1.endsWith("lo"));//true
System.out.println(str1.endsWith("o"));//true
}
}
**练习:**用户登录功能
/**
* 模拟用户登录的操作
* 期望用户键盘录入用户名和密码和验证码
* 然后跟我们所定义的信息(登录信息)进行比较
* 如果都相同,则提示登录成功
* 如果不相同,给用户三次机会,但是三次机会都登录不成功,让它联系管理员(自行扩展)
* 注意:验证码不区分大小写
*/
public class Demo01 {
public static void main(String[] args) {
String username = "root";
String password = "123456";
String yzm = "123tT";
int i = 3;
while (login(username,password,yzm)){
if (--i == 0){
System.out.println("你还剩余"+0+"次机会");
break;
}
System.out.println("你还剩余"+i+"次机会");
}
if (i>0){
System.out.println("登录成功");
}
}
public static boolean login(String username,String password,String yzm){
Scanner scanner = new Scanner(System.in);
System.out.println("请输入用户名:");
String name = scanner.next();
System.out.println("请输入密码:");
String pad = scanner.next();
System.out.println("请输入验证码");
String yzm2 = scanner.next();
if (name.equals(username) && pad.equals(password) && yzm2.equalsIgnoreCase(yzm)){
return false;
}
return true;
}
}
10.4.2 String类的获取功能
length
功能:获取字符串对象中字符的个数也就是字符串的长度
用例
//int length()
public class Test02String {
public static void main(String[] args) {
String str1 = "hello";
System.out.println("该字符串的长度为:"+str1.length());//5
}
}
charAt
功能:获取字符串中指定索引位置的字符
用例
//char charAt(int index)
public class Test02String {
public static void main(String[] args) {
String str1 = "hello";
System.out.println(str1.charAt(2));//l
}
}
indexOf
功能:获取字符串中第一次出现的索引,没有返回-1
用例
//int indexOf(String str)
public class Test02String {
public static void main(String[] args) {
String str1 = "hello";
System.out.println(str1.indexOf('l'));//2
}
}
lastIndexOf
功能:获取str在字符串对象当中最后一次出现的索引位置
用例
//int lastIndexOf(String str)
public class Test02String {
public static void main(String[] args) {
String str1 = "hello";
System.out.println(str1.lastIndexOf('l'));//3
}
}
substring(int start)
功能:从start索引位置开始截取,到末尾结束
用例
//String substring(int start)
public class Test02String {
public static void main(String[] args) {
String str1 = "hello";
System.out.println(str1.substring(1));//ello
}
}
substring(int start,int end)
功能:从start索引位置开始截取到end结束(含头不含尾)
用例
//String substring(int start,int end)
public class Test02String {
public static void main(String[] args) {
String str1 = "hello";
System.out.println(str1.substring(1,3));//el
}
}
练习1:字符串的遍历
public class Demo03 {
public static void main(String[] args) {
String str = "sdf1g351f3ag1";
for (int i = 0; i < str.length(); i++) {
System.out.print(str.charAt(i)+"\t");//s d f 1 g 3 5 1 f 3 a g 1
}
}
}
练习2:统计键盘录入的字符串对象中不同种类字符的个数
public class Demo02 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入字符串:");
String str = scanner.next();
int[] counts = new int[4];//0表示小写字母;1表示大写字母;2表示数字;3表示其它
for (int i = 0; i < str.length(); i++) {
char c = str.charAt(i);
if (c <= 'z' && c >= 'a'){
counts[0]++;
}else if (c <= 'Z' && c >= 'A'){
counts[1]++;
}else if (c >= 48 && c <= 57){
counts[2]++;
}else {
counts[3]++;
}
}
System.out.println("0表示小写字母;1表示大写字母;2表示数字;3表示其它");
System.out.println(Arrays.toString(counts));
}
}
10.4.3 String类的转换功能
toCharArray
功能:把字符串对象转换为字符数组
//char[] toCharArray()
public class Test02String {
public static void main(String[] args) {
String str1 = "hello";
char[] chars = str1.toCharArray();
System.out.println(Arrays.toString(chars));//[h, e, l, l, o]
}
}
toLowerCase
功能:把字符串对象中所有的字符转换为小写
//String toLowerCase()
public class Test02String {
public static void main(String[] args) {
String str1 = "HELLo";
System.out.println(str1.toLowerCase());//hello
}
}
toUpperCase
功能:把字符串对象中所有的字符转换为大写
//String toUpperCase()
public class Test02String {
public static void main(String[] args) {
String str1 = "hello";
System.out.println(str1.toUpperCase());//HELLO
}
}
练习:键盘录入字符串,把录入字符串的首字母转换为大写,其他的小写。
public class Demo04 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入字符串:");//hello
String str = scanner.next();
String str1 = str.substring(0,1).toUpperCase();
String str2 = str.substring(1).toLowerCase();
System.out.println(str1+str2);//Hello
}
}
10.4.4 其它功能
trim
功能:去除字符串两端空格
//String trim()
split
功能:按照指定符号分割字符串
//String[] split(String str)
replace
功能:将字符串对象中所有的字符oldChar替换为字符newChar,并返回新的字符串
//String replace(char oldChar, char newChar)
replaceAll
功能:将字符串对象中所有的字符串regex替换为字符串replacement,并返回新的字符串
//String replaceAll(String regex, String replacement)
replaceFirst
功能:将字符串对象中第一个字符串regex替换为字符串replacement,并返回新的字符串
//String replaceFirst(String regex, String replacement)
用例:
public class Test02String {
public static void main(String[] args) {
String str = "ab43a2c43d";
System.out.println(str);
System.out.println(str.replaceFirst("43aa","opp"));//ab43a2c43d
System.out.println(str.replace("3ac","fty"));//ab43a2c43d
System.out.println(str.replaceAll("\\d","f"));//abffafcffd
}
}
contains
功能:判断字符串对象当中是否包含参数s
//boolean contains(CharSequence s)
isEmpty
功能:判断字符串对象当中内容是否为空,就算字符串中有空格也是不为空
//boolean isEmpty()
checkBounds
检查数组是否越界
offset:开始位置
length:元素长度
源码
private static void checkBounds(byte[] bytes, int offset, int length) {
if (length < 0)
throw new StringIndexOutOfBoundsException(length);
if (offset < 0)
throw new StringIndexOutOfBoundsException(offset);
if (offset > bytes.length - length)
throw new StringIndexOutOfBoundsException(offset + length);
}
codePointAt
获取字符串指定位置的字符,返回对应的ASCII码
10.5 字符串的拓展和思考
为什么直接输出字符串对象可以得到字符串值,而其它引用类型的对象是地址值
因为在输出对象时,都会自动调用对象的toString()方法
//Object类中的toString()方法
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
而Object类是所有类的父类,所以会调用Object类中的toString()方法
但是String类重写了toString()方法,所以会返回它本身的字符串值
//String类中的toString()方法
public String toString() {
return this;
}
为什么java1.7及以前常量池存放在方法区里,1.8及以后存放在堆里面
因为永久代里面的生命周期太长,常量存放在永久代会浪费内存空间。而堆里面有垃圾回收机制,常量会被自动回收,所以常量池由方法区转到了堆里面
10.6 StringBulder
伸缩性不足:由于多次扩容造成的内存浪费问题