Java常量与变量
常量
常量类型
- 字符串常量
'abc','hello' - 整数常量
100,200 - 浮点数常量
2.5,3.14 - 字符常量:单引号引起来的单个字符
'a','w','2','啊'可以是汉字,但是不能什么都没有 - 布尔常量
false,true - 空常量
null代表没有任何数据(null不能用来打印输出)
基本数据类型
| 数据类型 | 关键字 | 内存占用 | 取值范围 |
|---|---|---|---|
| 字节型 | byte | 1字节 | -128 ~ 127 |
| 短整型 | short | 2字节 | -2^15 ~ 2^15-1 |
| 整型 | int | 4字节 | -2^31 ~ 2^31-1 |
| 长整型 | long | 8字节 | -2^63 ~ 2^63-1 |
| 单精度浮点型 | float | 4字节 | 超大 |
| 双精度浮点型 | double | 8字节 | 超级大 |
| 字符型 | char | 2字节 | 0~65535(2^16-1) |
| 布尔类型 | boolean | 1字节 | true & false |
整数型 byte short int long
浮点型 float double
字符型 char
布尔型 boolean
注意:
- 字符串不是基本类型,是引用类型
- 浮点型可能只是近似一个值,而并非是其精确值
- 数字范围和字节数不一定相关,例如
float和long - 浮点数默认类型是
double,用float,得加F后缀 - 整数默认为
int,用long,要加L后缀
变量
定义变量注意他们的取值范围:
1 | byte x = 10; //正确 |
注意long,要加L
1 | long x = 300000000000; // 错误 |
注意float,要加F
1 | float x = 2.5; //错误 |
注意事项:
- 变量名不能重复
float和long要加后缀- 未赋值的变量不可以使用
- 使用
byte和short要注意取值范围 - 变量不要超出变量的取值范围
- 一定要先声明后使用,java是从上到下执行的
数据类型转换
自动类型转换(隐式)
- 自动完成
- 数据范围从小到大转换
- 布尔类型在JAVA中就是布尔,不能运算,不是0,1
强制转换
1 | int num = (int)100L; //带上括号就可以实现强制转换 |
注意:
- 强制类型转换要谨慎使用,易发生精度损失、数据溢出
byte/short/char都可以数学运算,都会被首先提升为int来进行运算在运算过程中,他们会变为1
2
3
4
5byte x = 60;
byte y = 50;
byte result = x + y;
//这样会报错
//byte + byte -> int + int -> int != byteint,可以改成
1 | int result = x + y; |
或者
1 | byte result = byte(x + y); |
运算符
运算符类型
加减乘除 +-*/
取模 %只对于整数有效
自增自减 ++ --
赋值运算符 =
比较运算符 > < == <= >=
逻辑运算符 && || !
三元运算符 ?:
- 加法多种用法
- 加法的运算结果是运算范围最大的那一个
char类型的加法会变为int- 可以用于字符串相加减
- 逻辑运算符
逻辑运算符||和&&有短路效果,
如果根据左边已经可以判断得到最终的结果,右侧的代码将不会执行
- 赋值运算符
赋值运算符隐含一个强制的转换类型
方法
定义格式
1 | public static void xxx(){ |
调用方法
- 单独调用
function() - 打印调用
System.out.print(function() - 赋值调用
int x = function
注意:void的方法只能单独调用,也可以使用return;直接返回
同c++,可以根据数据类型的不同和参数的数量不同来实现重载。
唯一的区别–可以通过多类型的顺序区别
1 | sum(int a,double b); |
语句结构
基本内容同c语言
区别:switch(x)其中x只能为
基本数据类型:byte/short/char/int
引用数据类型:String/enum
数组
数组是一种引用数据类型
- 特点:
- 一个数组中的数据类型必须一致
- 数组的长度在运行期间长度不可改变
- 初始化:
- 动态初始化——指定长度
- 静态初始化——指定内容,自动推算长度
- 创建:可以化为一步
1
2
3String[] str;
str = new String[5];//动态
str = new String[]{"a","b","c"};//静态1
2
3
4int[] sum = new int[50];
//动态指定长度创建数组
String[] arr = new String[]{"你好啊","你是谁啊"};
//静态指定数组内容创建数组
当使用动态初始化数组的时候,其中的元素会自动拥有一个默认值(java的这个特点让我们可以使用数组时不用初始化了)
1 | 整数-->0 |
数组长度:直接
.length即可1
System.out.println(arr.length)
访问:
直接访问数组名,得到该数组对应的内存地址哈希值
1 | [I@1b6d3586 |
传递数组参数
1
public static void fun(int[] xxx){}
引用:当一个数组赋值给数组时,他们指向同一片内存
1
2
3
4
5int[] arr = new int[5];
int[] arr2 = arr;
arr[0]=1;
System.out.println(arr[0]);// 1
System.out.println(arr2[0]);// 1数组的长度不可变
1
2
3
4
5
6
7int[] arr = new int[5];
System.out.println(arr.length);//5
arr = new int[10];
System.out.println(arr.length);//10
//这样看似是更改了数组的长度
//其实是创建了一个新的长度的数组
//更改了原先数组指向的地址
关于void是不是基本类型
当我们提到java的基本类型时,一般都说是八种。但在Thinking in java 这本java圣经中,将void归为基本类型。
倒也可以说得通:
基本类型和引用类型的区别是:
- 基本类型: 数据在栈中划分内存,值存储在栈上
- 引用类型: 数据在栈中划分一块内存用来存储其引用(地址),而其真正的数据存放在堆中
而void不可以new出来(因为 void 的源码中,将构造函数设置为 private 的,所以外部不能 new 对象),void也是存储在栈上的,所以将它归为了基本类型
在Java的Class反射内有一个方法isPrimitive() ,可以判断是否是基本类型,当我们输入代码void.class.isPrimitive(),它的返回值是true,再一次证明void分为基本类型是有道理的
static关键字
静态成员数据
在类中,加了static的关键字,意味着该变量不再属于某一个成员,而是属于类本身,该类的所有对象共用一个静态数据
调用:
1 | Person stu1 = new person(); |
静态代码块
1 | static{ |
特点:
- 静态代码块只会在类加载时执行唯一的一次
- 是用来对属性赋值的
- 静态内容先于非静态
注意:
- 静态成员函数只能访问静态成员变量,非静态函数可以访问所有的内容
- 没有this指针
覆盖重写
方法的覆盖重写(override):
方法的名称一样,参数列表也一样此时会发生方法的覆盖重写
特点:因为创建时创建的对象是子类的,所以在调用方法时,一定会先调用子类中覆盖重写的函数
注意:
- 必须保证名称相同,参数列表相同
@override写在覆盖重写的函数前,用来检测是否成功的覆盖重写- 子类方法的返回值必须小于等于父类的返回值范围
- 子类方法的权限必须大于等于父类方法的权限修饰符(
public>protected> (default)(即什么都不写) >private)
1 | //假设此类已经投入属于,我们要添加功能,不要对此类进行更改 |
1 | //新建一个新的类,重写要增加功能的方法 |
super
用法:
- 在子类的成员方法中,访问父类的成员变量
- 子类中访问父类的成员方法
- 子类的构造方法中,访问父类的构造方法
super();
转型
无论是上转还是下转都是为了让类的使用范围和适用范围发生变化,以便操作不同范围的变量或者方法。
向上转型
1 | father up = new son();//向上转型 |
上转型是指将子类对象使用父类引用进行引用。
特点:
上转型对象可以操作和使用子类继承或者重写的方法
上转型对象不能使用子类的新方法或者变量
向上转型一定是安全的,因为是小范围想大范围的转换
向下转型
与向上转型相反,即是把父类对象转为子类对象:作用也与上转相反
向下转型的应用并不多
特点:
是向上转型的一个还原过程,强制转换会导致报错
向下转型不安全,因此应保证原本该对象就是子类的一个内容,不能指鹿为马
什么意思呢?
1 | int num = (int) 10.0;//正确,10.0本身其实就是一个整型 |
示例
1 | public static void main(String[] args) { |
instanceof的使用
向下转型的过程中我们可能会失败报错,所以一定要先判断
格式: 对象名 instanceof 类名
1 | public static void main(String[] args) { |
final关键字
final关键字,代表不可改变的
用法:
修饰一个类,方法,局部变量,成员变量
修饰类
1 | public final class father |
final类不能有任何的子类(太监类),final类中所有的成员方法不可被覆盖重写
修饰方法
1 | public final void teach(){ |
不管是类还是方法,final和abstract只能使用一个
修饰局部变量
final局部变量修饰完成,则其不能再被更改(类似C++的const)
1 | final int x ; |
只需要保证有一次赋值即可
注意:
对于基本类型,
final表示变量的数据不可以改变对于引用类型,
final表示变量的地址值不可以改变(其中的值可以变)
修饰成员变量
由于成员变量有默认值,所以用了final之后必须初始化!
- 直接赋值
- 用构造函数赋值
1 | final String name; |
权限修饰符
java中有四种修饰符
| 权限符 | public | protected | (default) | private |
|---|---|---|---|---|
| 同一个类 | Yes | Yes | Yes | Yes |
| 同一个包 | Yes | Yes | Yes | No |
| 不同包子类 | Yes | Yes | No | No |
| 不同包非子类 | Yes | No | No | No |
内部类
一个类的内部包含另一个类
分类:
- 成员内部类
- 局部内部类
成员内部类
成员内部类:
格式:
1 | 修饰符 class 外部类名称{ |
1 | public abstract class father { |
内部类用外部类可以随意使用,但是外部类调用内部类需要建立内部类的对象。
使用成员内部类
- 间接方法
(通过外部类的对象,调用外部类的方法,里面间接在再调用内部类的方法) - 直接方法
(格式:外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类)
1 | father.fatherIn innerClass = new father().new fatherIn(); |
使用成员内部类的同名变量
1 | public class father { |
格式:外部类名.this.变量名
局部内部类
定义在方法内部的类
只能调用方法来使用,离开方法将不能使用它。
1 | public static void main(String[] args) { |
主函数
1 | public class father { |
局部内部类
局部内部类的final问题
java8+开始,只要局部变量内部没有发生实质性的变化,那么final是可以省略的
1 | class innerClass{ |
原因:new创建的对象是在堆内存当中的,局部变量是在栈内存当中的
所以在方法内的局部内部类,在调用后,方法会入栈出栈,但是对象仍然会存在,所以这个变量的值是不可以改变的
该对象调用此变量的时候会复制一份给自己使用,但是如果变量发生变化,就会报错
匿名内部类
也是一种局部内部类
如果接口的实现类只需要使用唯一的一次,那么这种情况下就可以省略掉该类的定义,改为使用匿名内部类。
接口:
1 | 接口名称 对象名 = new 接口名称(){ |
1 | public static void main(String[] args) { |
注意:
- 匿名内部类在创建对象的时候,只能使用唯一的一次
- 如果想多次使用,请使用实现类
- 匿名内部类省略了实现类/子类名称,匿名内部类是有名字的,但是匿名对象省略了对象名
内部类中权限修饰符的使用
- 外部类: public/default
- 成员内部类: public/default/protected/private
- 局部内部类: default
default是不需要写的意思
包装类
日常使用的基本数据类型,使用起来方便,但是没有对应的方法来操作这些基本类型的数据。
可以使用一个类,把基本类型的数据装起来,在类中定义一些方法,这个类就叫包装类。
包装类可以把基本类型变为对象类型,**方便使用ArrayList**,因为ArrayList是无法填入基本类型的
| 基本类型 | 对应的包装类 |
|---|---|
| byte | Byte |
| short | Short |
| int | Integer |
| long | Long |
| float | Float |
| double | Double |
| char | Character |
| boolean | Boolean |
只有int类型和char类型的包装类名字是Integer和Character这两个,其他全是开头字母大写
装箱和拆箱
装箱
把基本类型的数据包装到包装类中(基本类型的数据->包装类)
构造方法:(整型举例)
Integer(int value)构造一个新分配的Integer对象,他表示指定的int值Integer(String s)构造一个新分配的Integer对象,他表示String参数所指示的int值。传递的字符串必须是基本类型的字符串,否则会抛出异常,eg:“100”正确 “a”错误
静态方法:
static Integer valueOf(int i)返回一个表示指定int值的Integer实例对象static Integer valueOf(String s)返回保存指定的String的值的Integer对象
1 | public static void main(String[] args) { |
拆箱
在包装类中去除基本类型的数据(包装类->基本数据类型)
成员方法:
1 | int intValue() |
1 | // 构造方法 |
自动装箱和拆箱
1 | // 自动装箱 |
自动装箱和拆箱直接会在ArrayList中使用
1 | ArrayList<Integer> list = new ArrayList<>(); |
基本类型与字符串类型之间的相互转换
- 基本类型 -> 字符串
- 基本类型 + “” (空字符串)
- 使用包装类的静态方法
toString(参数),不是Object的toString方法 - String类的静态方法
valueOf(参数)
- 字符串 -> 基本类型
- 使用包装类的静态方法
parseXXX("数值类型的字符串")
- 使用包装类的静态方法
1 | // 基本类型 -> 字符串 |