第一章、Java入门
1、转义字符
- \t :一个制表位,实现对齐的功能
- \n :换行符
- \\:一个\
- \" :一个"
- \' :一个'
- \r :一个回车
练习:测试一下。
2、Java数据类型
分类:
整数类型
浮点类型
说明
浮点数=符号位+指数位+尾数位。
浮点类型的尾数部分可能丢失,造成精度损失,比如:2.7 和 8.1 / 3是不一样的。
比如:
double num1 = 2.7;
double num2 = 8.1 / 3;
if(num1 == num2){
System.out.println("二者相等");
}
//这句话是不会输出的
如果想让它能输出可以采用函数:
double num1 = 2.7;
double num2 = 8.1 / 3;
if(Math.abs(num1 - num2) < 0.00001){//这是判断二者相减的绝对值
System.out.println("二者相等");
}
浮点类型也可以采用科学计数法的方式:5.12e2 = 5.12 × 10 ^ 2。
字符类型
允许写这种方式:
char c1 = 97;
char c2 = '\t';
java中char的本质就是一个整数,输出时输出的是它对应的编码。所以char类型是可以进行计算的。
字符类型存储到计算机的过程:
'a'--->字符编码是97--->二进制是1100001--->存储
从计算机读取的过程:
二进制1100001--->字符编码97--->'a'--->显示
布尔类型
boolean = true or false
布尔类型的值占用1个字节
3、字符集与字符编码(♥♥♥)
计算机中储存的信息都是用二进制数表示的;而我们在屏幕上看到的英文、汉字等字符是二进制数转换之后的结果。通俗的说,按照何种规则将字符存储在计算机中,如'a'用什么表示,称为"编码";反之,将存储在计算机中的二进制数解析显示出来,称为"解码",如同密码学中的加密和解密。在解码过程中,如果使用了错误的解码规则,则导致'a'解析成'b'或者乱码。
-
ASCII字符编码:主要包括控制字符(回车键、退格、换行键等);可显示字符(英文大小写字符、阿拉伯数字和西文符号)。
ASCII编码:将ASCII字符集转换为计算机可以接受的数字系统的数的规则。使用7位(bits)表示一个字符,共128字符;但是7位编码的字符集只能支持128个字符,为了表示更多的欧洲常用字符对ASCII进行了扩展,ASCII扩展字符集使用8位(bits)表示一个字符,共256字符。
-
Unicode字符编码:包含了非常多的字符,每个字符的编码都不同,所以不会有乱码问题。一个英文字母和汉字都占用2个字节,可能会存在冗余。
-
UTF-8字符编码:是一种针对Unicode的可变长度字符编码(定长码),也是一种前缀码。它可以用来表示Unicode标准中的任何字符,且其编码中的第一个字节仍与ASCII兼容,现在使用最广泛。字母占1个字节,汉字占3个字节。
4、数据类型转换
自动类型转换
细节:
- 多种类型数据计算的时候,会把它们先转换成类型最大的那个
- (byte、short)和char之间不会进行自动类型转换
- byte、short、char类型之间可以进行计算,但先要转成int类型
- 自动提升原则:计算后,结果转成类型最大的那个
强制类型转换
将容量大的数据类型转换为容量小的数据类型。使用时要加上强制转换符 ( ),但可能造成 精度降低或溢出,格外要注意。
细节:
- 强转符号只针对最近的操作数有效,所以一般会在后面数据操作加上括号
- char类型可以保存int类型的值,但如果保存int类型的变量需要强转
数据类型和String类型转换
//基本类型转String
String s1 = 某基本数据类型的值 + "";
//String转基本数据类型,调用基本数据类型包装类.parse方法即可,如下:
int i1 = Integer.parseInt("123");
//注意String类型不能用这种方式转char类型,因为字符串包含多个字符,可以使用charAt方法
char c1 = s1.charAt(index);
5、运算符
运算符分类
- 算术运算符
- 赋值运算符
- 关系运算符 [比较运算符]
- 逻辑运算符
- 位运算符 [需要二进制基础]
- 三元运算符
算数运算符
说明
除号/,如果10/3结果是整型不是浮点型
对一个数据取模,比如a % b,等同于a - a / b * b
或者可以这么认为,取模的符号和左边的数相互一致:
7 % 3 = 1;-7 % 3 = -1;7 % -3 = 1
经典习题
i = 10;
i = i++;
System.out.println(i);
赋值运算符
下面这些都是赋值运算符:
int a = 10;
a += 1;
a -= 1;
赋值运算符会进行类型转换:
byte a = 2;
a += 1;
//上面这个等价于 a = (byte)(a+1);
关系运算符
关系运算符的结果都是boolean类型,要么是true,要么是false。
逻辑运算符
逻辑与&,逻辑或|,逻辑异或^。
短路与&&,短路或 ||,取反!。
短路&&,只要第一个为false那后面不判断,短路或||,只要后面有一个为true后面不判断。
public static void main(String[] args) {
int x = 5;
int y = 5;
if (x++== 6 & ++y== 6){
}
System.out.println("x = " + x + " y = " + y);
}
//x = 6;y = 6
public static void main(String[] args) {
int x = 5;
int y = 5;
if (x++== 6 && ++y== 6){
}
System.out.println("x = " + x + " y = " + y);
}
//x = 6;y = 5
三元运算符
条件表达式 ? 表达式 1: 表达式 2;
为true执行表达式1,false执行表达式2。
public static void main(String[] args) {
int a = 10;
int b = 99;
int result = a > b ? a++ : b++;
System.out.println("result=" + result);
System.out.println("a=" + a);
System.out.println("b=" + b);
}
题目:计算三个数中最大的那个
public static void main(String[] args) {
int num1 = 270;
int num2 = 25;
int num3 = 104;
int bigNum1 = num1 > num2 ? num1 :num2;
int max = bigNum1 > num3 ? bigNum1 : num3;
System.out.println("最大的数是:" + max);
//或者
int max = (num1 > num2 ? num1 : num2) > num3 ? (num1 > num2 ? num1 : num2) : num3;
System.out.println("最大的数是:" + max);
}
位运算符
算术右移 >>:低位溢出,符号位不变,并用符号位补溢出的高位
算术左移 <<: 符号位不变,低位补 0
逻辑右移 >>>:也叫无符号右移,运算规则是: 低位溢出,高位补 0
运算符的优先级
其中最需要注意的就是:逻辑与、逻辑或、逻辑非的优先级
逻辑非 > 逻辑与 > 逻辑或
//好好看这个例子
boolean s1 = true,s2 = true,s3 = false;
System.out.println(s1 || s2 && s3);//true
6、进制换算
常用的进制的符号:
- 十进制就是直接写即可
- 八进制要求以0开头,比如:015
- 十六进制:0x开头,比如:0x15
- 二进制:要求0b或0B开头,比如0b11011
其他细节:
- 其他进制转换成十进制,按照每一位进行计算,最后一位是进制的0次方,依次往前算
- 十进制转换成其他进制,拿这个数除以换算的进制,取余数并且反向输出即可
- 二进制转八进制,每3位一组去计算即可
- 二进制转十六进制,每4位一组去计算
- 八进制转二进制:八进制每一位都换成二进制的三位
- 十六进制转二进制:同上,变成一位变四位
第二章、程序控制结构
1、顺序控制
就是按照程序代码的顺序执行。
2、分支控制(if)
分为以下:
if单分支、if-else双分支、if-else if...-else多分支、嵌套分支。
题目:判断一个年份是否是闰年,闰年的条件是符合下面二者之一:
(1)年份能被 4 整除,但不能被 100 整除;
(2)能被 400 整除
public static void main(String[] args) {
int year = 2100;
System.out.println(isRunNian(year) ? year + "是闰年" : year + "不是闰年");
}
public static boolean isRunNian(int year){
if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0){
return true;
}else{
return false;
}
}
题目:下面这个输出什么?
public static void main(String[] args) {
boolean b = true;
if (b == false){ //这里输出b,如果把 b==false 改成 b=false 输出c
System.out.println("a");
}else if (b){
System.out.println("b");
}else if (!b){
System.out.println("c");
}else{
System.out.println("d");
}
}
题目:参加歌手比赛,如果初赛成绩大于 8.0 进入决赛,否则提示淘汰。并且根据性别提示进入男子组或女子组, 输入成绩和性别,进行判断和输出信息。
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输出选手的成绩");
double score = scanner.nextDouble();
if (score >= 8.0) {
System.out.println("请输出选手的性别");
char gender = scanner.next().charAt(0);
if (gender == '男'){
System.out.println("进入男子组");
}else if (gender == '女'){
System.out.println("进入女子组");
}else {
System.out.println("性别输入有误");
}
}else{
System.out.println("你好,成绩不合格,你被淘汰了");
}
}
3、分支控制(switch)
语法:
流程图如下:
如果case语句种没有break,那么就继续执行下一个case中的语句,这个叫做穿透。
switch(表达式),表达式的返回值必须是byte、short、int、char、enums,String类型。
switch和if语句的区别?
-
如果判断的具体数值不多,而且符合 byte、 short 、int、 char, enum[枚举], String 这 6 种类型。虽然两个语句都可以使用,建议使用 swtich 语句。
-
其他情况:对区间判断,对结果为 boolean 类型判断,使用 if,if 的使用范围更广。
4、循环控制(for)
语法:
细节:
- for(;循环判断条件;) 中的初始化和变量迭代可以写到其它地方,但是两边的分号不能省略。
- 循环初始值可以有多条初始化语句,但要求类型一样,并且中间用逗号隔开,循环变量迭代也可以有多条变量迭代语句,中间用逗号隔开。
5、循环控制(while)
语法:
6、循环控制(dowhile)
语法:
循环变量初始化;
do{
循环体(语句);
循环变量迭代;
}while(循环条件);
先执行一次再判断,固定至少执行一次。
上面这些循环可以嵌套执行。
7、跳转控制(break、continue)
- continue语句用于结束本次循环,继续执行下一次循环
- continue 语句出现在多层嵌套的循环语句体中时,可以通过标签指明要跳过的是哪一层循环 , 这个和前面的标签的使用的规则一样.
- break 语句用于终止某个语句块的执行,一般使用在 switch 或者循环[for , while , do-while]中
第三章、数组
1、一维数组简介
数组是一种引用类型的数据结构,可以储存同一种类型的数据。
动态初始化数组:
//声明数组
int a[]; 或者 int[] a
//创建数组
a=new int[10];
静态初始化数组:
int[] a = {1,4,5,1,5,2,7};
细节
- 数组元素可以是引用类型,也可以是基本类型
- 创建的数组如果没赋值,是有默认值的
- 可能会报数组下标越界异常
练习:
- 创建一个 char 类型的 26 个元素的数组,分别 放置'A'-'Z'。使用 for 循环访问所有元素并打印出来。提示:char 类型数据运算 'A'+2 -> 'C'
- 请求出一个数组 int[]的最大值 {4,-1,9, 10,23},并得到对应的下标。
2、数组赋值机制和数组拷贝
数组在默认情况下是引用传递,赋的值是地址。
值传递/值拷贝 和 引用传递/引用拷贝的区别可以使用代码测试一下。
测试一下怎么让数组进行值拷贝。
3、数组反转
两种方式:
- 找规律反转
- 使用逆序赋值的方式
4、数组添加/扩容
练习1:实现动态的给数组添加元素效果,实现对数组扩容。ArrayAdd.java
- 原始数组使用静态分配 int[] arr = {1,2,3}
- 增加的元素 4,直接放在数组的最后 arr = {1,2,3,4}
- 用户可以通过如下方法来决定是否继续添加,添加成功,是否继续?y/n
练习2:有一个数组 {1, 2, 3, 4, 5}, 可以将该数组进行缩减,提示用户是否继续缩减,每次缩减最后那个元素。当只剩下最后一个元素,提示,不能再缩减。
5、数组排序与查找
排序
冒泡排序法、选择排序法。
查找
二分查找
练习:有一个数列:白眉鹰王、金毛狮王、紫衫龙王、青翼蝠王猜数游戏:从键盘中任意输入一个名称,判断数列中是否包含此名称【顺序查找】 要求: 如果找到了,就提示找到,并给出下标值。
6、二维数组
动态初始化和静态初始化,方式同上。
动态初始化数组:
//声明数组
int a[][]; 或者 int[][] a
//创建数组
a=new int[10][5];
静态初始化数组:
int[][] a = {{},{},{},{}};
练习:使用二维数组打印一个 10 行杨辉三角。
细节:
1、二维数组的声明方式有: int[][] y 或者 int[] y[] 或者 int y[][]
2、二维数组实际上是由多个一维数组组成的,它的各个一维数组的长度可以相同,也可以不相同
第四章、面向对象(OOP)
1、对象
- 类是抽象的,概念的,代表一类事物,比如人类,猫类.., 即它是数据类型.
- 对象是具体的,实际的,代表一个具体事物, 即 是实例.
- 类是对象的模板,对象是类的一个个体,对应一个实例
对象创建在内存中是怎么存在的:
在堆中开辟对象的内存空间,它的常量存储在方法区中,栈中存储存储这个对象的引用地址,这个变量名称指向堆中对象的内存地址。
对象的属性如果不赋值是有默认值的。
2、方法
方法在内存中的执行流程:
方法的作用:
- 提高代码的复用性
- 可以将实现的细节封装起来,然后供其他用户来调用即可
方法语法:
访问修饰符 返回数据类型 方法名(形参列表..) {
//方法体 语句;
return 返回值;
}
方法的传参机制:
- 基本数据类型传递的是值,值传递
- 引用数据类型传递的是地址,引用的值就是地址
克隆对象自己实操一下。
3、递归
递归就是方法自己调用自己
练习:
1、阶乘问题
2、斐波那契数列
3、有一堆桃子,猴子每天吃掉一半还多一个,吃了10天,发现最后剩下1个桃子,问原来有多少?
4、迷宫问题
5、汉诺塔
6、八皇后问题
4、方法重载
要求:
- 方法名相同
- 形参列表不同:数量、类型、顺序不一样都算
- 返回值无所谓
形参是可变参数的方法,语法如下:
访问修饰符 返回类型 方法名(数据类型... 形参名) {
//方法体;
}
可变参数可以是0-任意个,可变参数的实参可以是数组,而且本质也是个数组。
方法重载为什么不由返回值决定?
重载一般是编译阶段就已经确定好用哪个了,可是如果重载也可以由返回值决定,在程序都没运行完,怎么知道返回值是什么类型的呢?所以不成立,甚至有的方法都没有返回值,程序员本身某一行代码也不需拿到返回值,所以他就写了一行:
f();
谁知道这一行代码要调用的是有返回值的方法呢?还是void的方法呢?所以不能用返回值判断重载。
5、作用域
6、变量和常量
- 局部变量:不会赋初始值,作用域在方法里
- 成员变量:会赋初始值,作用域在对象上
- 静态变量:会赋初始值,作用域在类上
- 常量:final修饰的变量
7、构造器
也叫构造方法:
【修饰符】 方法名(形参列表){
方法体;
}
细节:
- 构造器的修饰符可以是public / protected / private
- 构造器没有返回值
- 构造器调用是由系统完成的,new的时候使用的就是构造器
- 构造器的作用是完成
新对象的初始化,而不是对象的创建,这个时候对象的空间已经存在了,只是完成对象的初始化工作 - 默认构造器是无参的,没写就给个无参的
class Dog{
//这就是默认构造器
Dog(){
}
}
可以使用反编译工具javap来查看:
javap <option> <class>
| 指令 | 含义 |
|---|---|
| -help --help -? | 输出此用法消息 |
| -version | 版本信息 |
| -v -verbose | 输出附加信息 |
| -l | 输出行号和本地变量表 |
| -public | 仅显示公共类和成员 |
| -protected | 显示受保护的/公共类和成员 |
| -package | 显示程序包/受保护的/公共类和成员 (默认) |
| -p -private | 显示所有类和成员 |
| -c | 对代码进行反汇编 |
| -s | 输出内部类型签名 |
| -sysinfo | 显示正在处理的类的系统信息 (路径, 大小, 日期, MD5 散列) |
| -constants | 显示最终常量 |
| -classpath | 指定查找用户类文件的位置 |
对象创建的流程:
- 加载类信息(Person.class)只加载一次
- 在堆空间分配空间(内存地址)
- 完成初始化对象(先进行默认初始化:赋默认值,然后显示初始化:赋自定义的值)
- 把堆中的地址返回给栈中的变量名(也就是让遥控器person,直接指向对象,以后可以由遥控器调用对象)
8、this
字符串常量池指向堆中
-
this等于创建对象的时候,单独创建了一个空间指向了自己
-
this 关键字可以用来访问本类的属性、方法、构造器
-
访问构造器语法:this(参数列表); 注意只能在构造器中使用(即只能在构造器中访问另外一个构造器, `必须放在第一
条语句`)
public class ThisTest {
public static void main(String[] args) {
Boy boy = new Boy("张三",20);
System.out.println("当前对象的哈希值:" + boy.hashCode());
}
}
class Boy{
String name;
int age;
public Boy(){
}
public Boy(String name,int age){
//必须放在第一条语句
this();
this.name = name;
this.age = age;
System.out.println("this对象的哈希值:" + this.hashCode());
}
}