我理解相对物的局限,我尊重相对物的命运。
我把你的还给你,我把我的拿回来。
尘归尘,土归土,你归你,我归我。
java查漏补缺
0 8大基本数据类型及包装类
Java 八大基本数据类型以及包装类的详解
一、java中八种基本数据类型对应的包装类型:
原始类型 | 包装类 | 原始类型所占的字节数 |
---|---|---|
short | Short | 2个字节 |
int | Integer | 4个字节 |
long | Long | 8个字节 |
float | Float | 4个字节 |
double | Double | 8个字节 |
byte | Byte | 1个字节 |
char | Character | 2个字节 |
boolean | Boolean | 这个试编译环境而定 |
思考:java中提供的八种基本数据类型不够用吗?为什么java中还要提供对应的包装类呢?
-
基本数据类型之间的相互转换不是都可以制动转换的,而你强制转换又会出问题,比如String类型的转换为int类型的,那么jdk为了方便用户就提供了相应的包装类。
-
便于函数传值
-
就是在一些地方要用到Object的时候方便将基本数据类型装换
例子:
// 声明一个名为 "Integer" 的公共类
public class Integer {
// 声明一个私有整型成员变量 i
private int i;
// 构造函数,接受一个整数参数 a
public Integer(int a) {
// 将成员变量 i 设置为传入的参数 a
i = a;
}
// 声明一个静态方法 parseToInt,没有参数
public static int parseToInt() {
// 返回当前类的静态成员变量 i
return i;
}
// 声明一个静态方法 valueOf,接受一个字符串参数 str
public static Integer valueOf(String str) {
// 在这里应该有一系列的逻辑操作,将字符串 str 转换为整数类型的 IntegerStr
// 最终创建一个新的 Integer 对象,传入转换后的 IntegerStr 作为参数
// 返回新创建的 Integer 对象
return new Integer(IntegerStr);
}
}
上面是jdk关于Integer的一个例子 比如Integer intg = Integer.valueOf(str); int i = intg.parseToInt();
这样用户就很方便的完成了 String和int的转换 这样就方便了用户
有时候一个函数需要传递一个Object的变量 而你想传递int类型的进去显然不行,怎么办呢,用到了包装类。
public void test(Object obj){
}
你想传递5进去就可以这样
test(new Integer(5));
二,包装类的使用
以Integer为例子~
public class IntegerTest{
public static void main(String[] args){
//获取int类型的最大值和最小值
System.out.println("int最小值:" + Integer.MIN_VALUE);
System.out.println("int最大值:" + Integer.MAX_VALUE);
//以int推byte
System.out.println("byte最小值:" + Byte.MIN_VALUE);
System.out.println("byte最大值:" + Byte.MAX_VALUE);
//创建Integer类型的对象
Integer i1 = new Integer(10); //int--> Integer
Integer i2 = new Integer("123"); //String --> Integer
System.out.println(i1); //10
System.out.println(i2); //123
//以下程序编译可以通过。但是运行的时候会报异常.数字格式化异常.
//虽然可以将字符串转换成Integer类型,但是该字符串也必须是"数字字符串".
//Integer i3 = new Integer("abcd"); //NumberFormatException
}
}
效果图:
Integer中常用的方法:
public class IntegerTest1{
public static void main(String[] args){
//int-->Integer
//基本数据类型-->引用类型
Integer i1 = new Integer(10);
//Integer-->int
//引用类型-->基本类型
int i2 = i1.intValue();
System.out.println(i2 + 1); //11
//重要:static int parseInt(String s);
//String-->int
int age = Integer.parseInt("25");
System.out.println(age+1); //26
//"abc"这个字符串必须是“数字字符串”才行.
//int price = Integer.parseInt("abc"); //NumberFormatException
//重要:static double parseDouble(String s);
double price = Double.parseDouble("3.0");
System.out.println(price+1.0); //4.0
//将int类型的十进制转换成2进制
String s1 = Integer.toBinaryString(10);
System.out.println(s1); //1010
//将int类型的十进制转换成16进制
String s2 = Integer.toHexString(10);
System.out.println(s2); //a
//将int类型的十进制转换成8进制
String s3 = Integer.toOctalString(10);
System.out.println(s3); //12
//int-->Integer
Integer i3 = Integer.valueOf(10);
//String--->Integer
Integer i4 = Integer.valueOf("10");
System.out.println(i3);
System.out.println(i4);
}
}
包装类的作用还是很强大的~
三.装箱和拆箱
在我们使用基本类型的包装类时,有时肯定需要在他们之间进行转换,例如:
Integer i = Integer.valueOf(10); //将int转换为Integer
int i2 = i.intValue(); //将Integer转换为int
1
2
这种把基本数据类型转换成包装类的过程就叫装箱,而把包装类转换成基本数据类型的过程自然就称为拆箱了;从Java SE5开始,为了减少开发人员的工作,Java提供了自动拆箱与自动装箱功能,如果要生成一个数值为10的Integer对象,只需要这样就可以了:
Integer i = 10; //系统自动调用Integer.valueOf()方法来进行装箱
int n = i; //系统自动调用Integer.intValue()方法进行拆箱
1
2
自动拆装箱大大节省了开发人员的精力,不用再关心什么时候需要拆箱和装箱。但是,在使用中还是有一些问题需要注意:
1)包装类型数值的比较
示例1:
Integer i1 = 100;
Integer i2 = 100;
Integer i3 = 200;
Integer i4 = 200;
System.out.println(i1==i2); //true
System.out.println(i3==i4); //false
1
2
3
4
5
6
7
这里我们普遍会认为两次判断结果都是false,因为在比较对象时判断的是对象的引用,而不是判断对象的值;那么这里为什么i1i2为true呢,看以下源码我们就知道了:
public static Integer valueOf(int i) {
//从下面IntegerCache实现中可得知IntegerCache.high默认为127
if(i >= -128 && i <= IntegerCache.high)
//也就是说当初始化赋值为-128~127时,会直接返回数组中对象的引用,不会创建新的对象
return IntegerCache.cache[i + 128];
else
return new Integer(i);
}
private static class IntegerCache {
static final int high;
static final Integer cache[];
static {
final int low = -128;
int h = 127;
if (integerCacheHighPropValue != null) {
int i = Long.decode(integerCacheHighPropValue).intValue();
i = Math.max(i, 127);
h = Math.min(i, Integer.MAX_VALUE - -low);
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
}
private IntegerCache() {}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
从源码可以看出,在通过valueOf()方法创建Integer对象时,如果赋值在-128~127之间,就返回指向IntegerCache.cache中已经存在的对象的引用;否则创建一个新的Integer对象。
上面i1和i2的数值为100,因此会直接从cache中取已经存在的对象,所以i1和i2指向的是同一个对象,而i3和i4则指向新创建的不同对象。
示例2:
Double i1 = 100.0;
Double i2 = 100.0;
Double i3 = 200.0;
Double i4 = 200.0;
System.out.println(i1i2); //false
System.out.println(i3i4); //false
1
2
3
4
5
6
经过了上面Integer数值的比较,这里大家会条件反射的认为i1==i2应该输出true,但Double类的valueOf方法与Integer类的valueOf方法实现并不相同,因为在限定范围内整数的个数是有限的,而浮点数却不是。
注意,Integer、Short、Byte、Character、Long这几个类的valueOf()方法的实现是类似的,都存在-128~127的缓存池。Double、Float的valueOf方法的实现是类似的,不存在缓存池。
示例3:
Boolean i1 = false;
Boolean i2 = false;
Boolean i3 = true;
Boolean i4 = true;
System.out.println(i1i2); //true
System.out.println(i3i4); //true
1
2
3
4
5
6
7
因为Boolean底层valueOf()方法返回值始终是两个常量TRUE和FALSE的引用,所以这里输出都是true:
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);
1
2
3
4
5
6
2)包装类型运算及比较时的拆装箱
Integer a = 1;
Integer b = 2;
Integer c = 3;
Long d = 3L;
Long e = 2L;
System.out.println(c==(a+b)); //true
System.out.println(c.equals(a+b)); //true
System.out.println(d==(a+b)); //true
System.out.println(d.equals(a+b)); //false
System.out.println(d.equals(a+e)); //true
1
2
3
4
5
6
7
8
9
10
11
这里需要注意的是,当 "=="运算符的两个操作数都是包装类型的引用,则比较指向的是否为同一个对象;而如果其中有一个操作数是表达式(即包含算术运算)则比较的是数值(即会触发自动拆箱的过程)。另外,对于包装类型,equals()方法不会进行类型转换。
所以上面c==(a+b)中,a+b包含了算术运算,因此会触发自动拆箱过程,它们比较的是数值是否相等;
而c.equals(a+b)会先触发自动拆箱过程,再触发自动装箱过程,也就是说a+b,会先各自调用intValue方法,得到了加法运算后的数值之后,便调用Integer.valueOf方法,再进行equals比较。
同理对于后面的也是这样,不过要注意d.equals(a+b)和d.equals(a+e)输出的结果(如果数值是int类型的,装箱过程调用的是Integer.valueOf;如果是long类型的,装箱调用的Long.valueOf方法)。
四、对象池
在Java中,对象池优化主要适用于一些整数和字符的包装类,而不仅仅限于 int 类型。具体来说,整数和字符包装类在某些范围内会使用对象池来优化内存使用和性能。
这些包装类包括:
Integer: 范围 -128 到 127 之间的整数会使用对象池。
Byte: 范围 -128 到 127 之间的字节会使用对象池。
Short: 范围 -128 到 127 之间的短整数会使用对象池。
Character: 所有字符(Unicode值)都会使用对象池。
其他的包装类,比如 Long、Float、Double,并没有类似的对象池机制,每个值都对应一个新的对象。
五 ==和equals
3. 请选择正确的一项()
public static void main(String[] args) {
Integer i1 = 100;
Integer i2 = 100;
System.out.println(i1==i2);
}
□ A.编译错误
□ B.编译错误
☑C.控制台输出true
□ D.控制台输出false
作答结果:✓
正解:C
解析:
你创建了两个 Integer 类型的对象 i1 和 i2,并将它们都初始化为整数值 100。然后,你通过 == 运算符比较了这两个对象的引用。让我为你解释一下结果为什么是怎样的。
在 Java 中,对于 Integer 类型的对象,Java 会自动进行对象池优化,特别是在范围从 -128 到 127 内的整数。这意味着在这个范围内的整数值,每次都会返回同一个对象的引用,以减少内存使用。
因此,对于你的代码中的情况,i1 和 i2 都被赋值为 100,而且它们的值在对象池的范围内,所以它们会引用相同的 Integer 对象。这就是为什么 i1 == i2 的比较会返回 true。
然而,如果你将 i1 和 i2 的值设定为超出对象池范围的整数,比如 1000,那么 i1 == i2 的比较将返回 false,因为它们引用的将是不同的 Integer 对象。
java
------------------------------------------------------------------------------------------------------------------------
4.请选择正确的一项()
public static void main(String[] args) {
Double i1 = 100.0;
Double i2 = 100.0;
System.out.println(i1==i2);
}
□ A.编译错误
□ B.运行错误
□ C.
控制台输出true
☑D.
控制台输出false
作答结果:✓
正解:D
解析: 每个Double都是一个新的对象
创建了两个 Double 类型的对象 i1 和 i2,并将它们都初始化为 100.0。
然后,通过 == 运算符比较了这两个对象。让我来解释一下结果false:
在 Java 中,基本数据类型的比较可以使用 == 运算符,但对于对象类型(包括包装类,如 Double)的比较,== 运算符比较的是对象的引用(内存地址),而不是对象的内容(纯数值)。
在代码中,尽管 i1 和 i2 的值都是 100.0,但它们是不同的对象,它们有不同的内存地址空间中。因此,i1 == i2 的比较将会返回 false,因为它们引用的是不同的对象。
如果你想比较两个 Double 对象的值是否相等,应该使用 .equals() 方法。这个方法会只比较对象的内容,而不是严格地比较引用的地址。
------------------------------------------------------------------------------------------------------------------------
5.请选择正确的一项()
public static void main(String[] args) {
Integer i1 = 130;
Integer i2 = 130;
System.out.println(i1==i2);
}
□ A.编译错误
□ B.编译错误
□ C.控制台输出true
☑D.控制台输出false
作答结果:✓
正解:D
解析:
对于 Integer 类型的对象,Java 在一定范围内(-128 到 127)使用了对象池(也称为缓存),以优化内存使用和性能。在这个范围内,每个整数值对应一个唯一的对象,这意味着相同的值的 Integer 对象会引用相同的对象。
在你的代码中,130 并不在对象池范围内,因此 i1 和 i2 引用的是不同的 Integer 对象。因此,i1 == i2 的比较会返回 false,因为它们引用的是不同的对象。==比较的是不同对象的地址值。
------------------------------------------------------------------------------------------------------------------------
6.请选择正确的一项()
public static void main(String[] args) {
Integer i1 = 130;
Double i2 = 130.0;
System.out.println(i1==i2);
}
☑ A.编译错误
□ B.运行错误
□ C.控制台输出true
□D.控制台输出false
作答结果:✓
正解:A
//java: 不可比较的类型: java.lang.Integer和java.lang.Double
改为equals 则编译通过,输出false 因为 是两个不同的引用对象。
解析:
------------------------------------------------------------------------------------------------------------------------
7.请选择正确的一项()
public static void main(String[] args) {
Integer i1 = 130;
Double i2 = 130.0;
System.out.println(i1.equals(i2));
}
□ A.编译错误
□ B.运行错误
□ C.控制台输出true
☑D.控制台输出false
作答结果:✓
正解:D
解析:
------------------------------------------------------------------------------------------------------------------------
8.请选择正确的一项()
public static void main(String[] args) {
Integer i1 = new Integer(10);
Integer i2 = new Integer(10);
System.out.println(i1 == i2);
}
□ A.编译错误
□ B.运行错误
□ C.控制台输出true
☑D.控制台输出false
作答结果:✓
正解:D
解析: 使用 new Integer(10) 创建的 Integer 对象会在堆上创建不同的对象,而使用 Integer.valueOf(10) 可以利用对象池,使相同值的对象引用相同的对象,提高性能和内存效率。
------------------------------------------------------------------------------------------------------------------------
9.请选择正确的一项()
public static void main(String[] args) {
Integer i1 = 10;
Integer i2 = Integer.valueOf(10);
Integer i3 = new Integer(10);
System.out.println(i1 == i2);
System.out.println(i2 == i3);
}
□ A.控制台打印true和true
□ B.控制台打印false和false
☑C.控制台打印true和false
□ D.控制台打印false和true
作答结果:✓
正解:C
解析:
代码中,进行了不同方式创建 Integer 对象的比较,使用了自动装箱、静态工厂方法和显式地使用构造函数。让我为你逐一解释结果为什么是怎样的。
Integer i1 = 10;:这是自动装箱的方式,Java 会将基本数据类型的值自动转换为对应的包装类对象。在范围内的整数值(-128 到 127)会使用对象池优化,相同值的对象引用相同的对象,所以 i1 和 i2 都引用相同的对象,i1 == i2 的比较会返回 true。
Integer i2 = Integer.valueOf(10);:使用 Integer.valueOf 静态工厂方法创建 Integer 对象。与上面相同的值范围内,也会使用对象池优化,所以 i2 和 i1 引用相同的对象,i2 == i1 的比较同样会返回 true。
Integer i3 = new Integer(10);:显式地使用构造函数创建 Integer 对象。无论值是否在范围内,使用构造函数都会在堆上创建新的对象,所以 i3 引用一个不同的对象。因此,i2 == i3 的比较会返回 false,因为它们引用的是不同的对象。
------------------------------------------------------------------------------------------------------------------------
10.选择不是 Java语言中的基本数据类型的选项()
□ A.byte
□ B.long
☑C.String
□ D.char
作答结果:✓
正解:C
解析:
------------------------------------------------------------------------------------------------------------------------
11.package run;
public class Run{
/*题目:8个基本类型及包装类基础练习-1
*/
public static void main(String[] args) {
int i = 20;
//将int类型转换为包装类型
Integer wrapperi = __new___ Integer(i);
//将包装类型转换为基本类型
int j = __wrapperi___.intValue();
//将Integer 转换为String类型
String str = wrapperi.toString();
//将String 转换为Integer类型
Integer k = Integer.__valueOf___(str);
//将Integer 转换为Double类型
Double d = wrapperi.doubleValue();
}
}
请根据注释补全代码
作答结果:✓
正解:
①new
②wrapperi
③valueOf
解析:
12.package run;
public class Run {
/*题目:8个基本类型及包装类基础练习-2
需求:
1.定义静态方法getInteger,传入一参int类型,方法体返回对应Integer包装类型对象
2.定义静态方法getInt,传入一参Integer包装类型对象类型,方法体返回对应int值
3.定义静态方法getCharacter,传入一参char类型,方法体返回对应Character包装类型对象
4.定义静态方法getChar,传入一参Character包装类型对象类型,方法体返回对应char值
*/
public static Integer getInteger(int i){
return new Integer(i);
}
public static int getInt(Integer __wrapper___){
return wrapper.intValue();
}
public static Character getCharacter(char c){
return new Character(__c___);
}
public static char getChar(__Character___ wrapper){
__return___ wrapper.charValue();
}
}
请根据注释补全代码
作答结果:✓
正解:
①wrapper
②c
③Character
④return
解析:
13.package run;
public class Run {
/*题目:8个基本类型及包装类基础练习-3
需求:
1.定义静态方法getLongObject,传入一参long类型,方法体返回对应Long包装类型对象
2.定义静态方法getLong,传入一参Long包装类型对象类型,方法体返回对应int值
3.定义静态方法getBooleanObject,传入一参boolean类型,方法体返回对应Boolean包装类型对象
4.定义静态方法getBoolean,传入一参Boolean包装类型对象类型,方法体返回对应boolean值
*/
public static Long getLongObject(long l){
return new __Long___(l);
}
public static long getLong(Long wrapper){
return wrapper.longValue();
}
public static Boolean getBooleanObject(booleanb){
return _____ Boolean(b);
}
public static boolean getBoolean(Boolean wrapper){
return wrapper.__booleanValue___();
}
}
请根据注释补全代码
作答结果:☓
正解:
①Long
②new
③booleanValue
解析:
1. javac HelloWorld.java
java 运行.class文件
javac命令可以编译.java文件 编译.java 文件为 .c lass 文件—》javaC命令
2. 关于配置环境变量path作用的说法中,正确的是( )
- 在任意路径使用java和javac等命令
- 配置完path后,要重启cmd命令窗口才可生效
- 配置完环境变量path的情况下,在C:\Users\Administrator\Desktop路径使用java命令,优先查找path路径
3. classpath路径应设置为jdk安装路径下的bin路径
在Java中,classpath 是一个用于指定类文件(.class 文件)或资源文件(如配置文件、图像等)的路径。它不仅仅是设置为JDK安装路径下的bin路径,而是可以指定为包含类文件或资源文件的任何目录路径。
classpath 是一个重要的概念,它用于告诉Java虚拟机(JVM)在哪里查找类文件或资源文件以进行加载。有几种不同的方式可以设置 classpath。
命令行设置: 在运行Java程序时,可以使用 -cp 或 -classpath 参数来设置类路径。例如:
bash
Copy code
java -cp /path/to/classes MyApp
环境变量: 你可以设置一个名为 CLASSPATH 的环境变量来指定类路径。多个路径可以使用分号(Windows)或冒号(Linux/macOS)分隔。
在代码中设置: 在代码中,你也可以使用 System.setProperty(“java.class.path”, “/path/to/classes”); 来动态设置类路径。
通常情况下,将类文件和资源文件放在不同的目录中,并将这些目录都添加到 classpath 中,以便Java程序可以正确地加载它们。
总之,classpath 是用于指定类文件或资源文件位置的重要机制,你可以根据实际需要设置为适当的目录路径。
4 .下列关于说法正确的是( )
常用DOS命令,cd 是进入指定目录
常用DOS命令, cd …代表返回上路径
jdk,jre,jvm三者关系是jdk中包含jre,jre中包含jvm
3.关于Eclipse:
- eclipse 支持插件式开发
- 3.3版本之前,eclipse不能开发web项目
- eclipse支持多语言开发java、C++、c等
- SVN插件用于管理代码的版本(version)
总结eclipse:
Eclipse 是一个非常流行的集成开发环境(IDE),可以用于开发 Java 程序以及多种其他类型的应用程序。以下是 Eclipse 的一些主要特点和用途:
特点:
多语言支持:
Eclipse 不仅仅用于 Java 开发,还支持多种编程语言,如C/C++、Python、PHP等。
插件架构:
Eclipse 采用插件式架构,允许开发者安装和使用各种插件来扩展和定制 IDE 功能。
强大的调试工具:
Eclipse 提供了丰富的调试功能,允许逐步执行代码并检查变量的值和执行过程。
代码自动完成:
Eclipse 提供代码自动完成功能,可以大大提高编码的效率。
版本控制集成:
Eclipse 集成了版本控制系统,如Git、SVN等,方便团队协作和源代码管理。
“metadata” 文件
在许多软件开发环境中,特别是与代码编辑、项目管理和集成开发环境(IDE)有关的情况下,“metadata” 文件夹通常用于保存与工作区(Workspace)或项目相关的设置、配置和元数据信息。
使用eclipse物理删除时不一定同时会删除硬盘上的文件,取决于你的操作和设置。
在 Eclipse 或其他类似的集成开发环境中,删除文件时的行为取决于你的操作和设置。
通常情况下,Eclipse 删除文件时会有两个选项:逻辑删除和物理删除。
逻辑删除: 在逻辑删除情况下,Eclipse 会将文件标记为已删除,但不会立即从硬盘上移除。你可能不再在工作区中看到这个文件,但它实际上仍然存在于你的文件系统中。
物理删除: 物理删除是指文件会被彻底从硬盘上删除,无法恢复。在 Eclipse 中进行物理删除时,文件会被彻底移除,而不是仅仅在工作区中标记为已删除。
默认情况下,Eclipse 在删除文件时可能会执行逻辑删除,将文件移到回收站或 Trash 文件夹中,而不会立即从硬盘上删除。这是为了防止意外删除造成不可恢复的损失。如果你想要彻底删除文件,通常可以使用 Shift + Delete 键盘组合或选择 “彻底删除” 选项来执行物理删除。
然而,确切的行为可能会因 Eclipse 版本、操作系统以及配置而有所不同。在执行删除操作时,最好在确认对话框中查看选项以确保你了解将要执行的操作。如果你关心文件的安全,请务必备份重要文件,以防万一。
- 导入maven项目 跟一般项目方式相似,但不同。
- 导入既存项目时可以拷贝整个工作空间
- 导入既存项目时可以拷贝项目,**进行import导入操作 **
Java语言提供了八种基本类型。
六种数字类型(四个整数型byte short int long long a = 100000L,两个浮点型float double 例子:float f1 = 234.5f。
),
一种字符类型, char
还有一种布尔型。 boolean
整数的默认类型是 int。
小数默认是 double 类型浮点型,
在定义 float 类型时必须在数字后面跟上 F 或者 f。
数据类型转换
整型、实型(常量)、字符型数据可以混合运算。运算中,不同类型的数据先转化为同一类型,然后进行运算。
数据类型转换必须满足如下规则:
- 不能 对boolean类型进行类型转换。
- 不能 把对象类型转换成不相关类的对象。
- 在把容量大 的类型转换为容量小的类型时必须使用强制类型转换。
而且 ,转换过程中可能导致溢出或损失精度,例如:
int i =128;
byte b = (byte)i;
- 浮点数到整数的转换是通过舍弃小数得到,而不是四舍五入,例如:
(int)23.7 == 23;
(int)-45.89f == -45
因为 byte 类型是 8 位,最大值为127,所以当 int 强制转换为 byte 类型时,值 128 时候就会导致溢出。
5. 浮点数到整数的转换是通过舍弃小数得到,而不是四舍五入,例如:
(int)23.7 == 23;
(int)-45.89f == -45
数据的从低级到高级。 自动类型提升
低 ------------------------------------> 高
byte,short,char—>int—>long—>float—>double
强制类型转换后
byte, short,char -> int -> long -> float -> double
//double到int 高到低
System.out.println((int)23.7); //23丢失精度
char c = 'a';// char< int 好事 自动提升容量 字符自动提升为int数字
int n = c+1;
System.out.println(n); //98
//但是 当int 数字 向下转换为 char 要强行剥夺 int的高级 才能转换为 char
System.out.println((char)n); //b
5. 控制台输出结果()
int a = 1;
double b = 1.2;
// a = 0;
System.out.println(a+b);
输出结果应为:2.2。因为在这段代码中,整数类型的a和浮点数类型的b相加,Java会自动进行类型提升,将a转换为浮点数,然后再进行加法运算。
6.int a = 4; a++ ++a +=
i++ 与 ++i 的主要区别有两个:
2.1、i++ 直接返回原来的值,++i 返回加1后的值。
2.2、i++ 不能作为左值,而++i 可以。
a:量子改变,一变百变。
执行以下五种情况
(1) a += a++; //a=?
(2) a += ++a; //a=?
(3) ++a += a; //a=?
(4) ++a += a++; //a=?
(5) ++a += ++a; //a=?
运算优先级
前缀++a/–a优先级大于所有数值运算符
后缀a++/a–优先级小于所有数值运算符
答案:
(1) a + a = a8 +1 = a9 //9
(2) a++ = a5 + a5 = a10 //10
(3) a+1=a5 +a5 = a10//10
(4) (a+1 = a5 + a5=a10)+1=a11 //11
(5) (a+1 = a5 +1 = a6 + a6 = a12 //12
详细解释:
(1) a += a++;
后缀++的优先级小于+=的优先级,因此先执行a+=a的操作,得8,然后再执行a++的操作,结果为9
(2) a += ++a;
前缀++的优先级大于+=的优先级,因此先执行++a的操作,得5,然后再执行a+=a的操作,结果为10
(3) ++a += a;
前缀++的优先级大于+=的优先级,因此先执行++a的操作,得5,然后再执行a+=a的操作,结果为10
(4) ++a += a++;
优先级:前缀++ > += > 后缀++,先执行++a得5,然后执行a+=a得10,最后执行a++得11
(5) ++a += ++a;
对于同时前缀的,顺序是从左往右,因此先执行左边的++a得5,然后执行右边的++a得6,最后执行a+=a得12
7. a–和–a的区别.
a-- 是先引用后减少,先在a所在的表达式中使用a的当前值,后让a减1;
–a 是先减少后引用,让a先减1,然后在a所在的表达式中使用a的新值
8. int a = 6; double b = 6.0; ==时 自动向上提升
System.out.println(a == b);//true
System.out.println(a == b);:这一行使用 == 运算符来比较变量 a 和 b 是否相等。由于 a 是整数,而 b 是浮点数,它们的数据类型不同,所以在比较时会发生类型转换。在这种情况下,整数 a 会被自动转换为浮点数,然后进行比较。因此,比较的实际是 6.0 == 6.0,这个条件是成立的。
9. & 和 &&
& 逻辑与运算符:
即使左边的表达式为 false,右边的表达式也会被计算,这可能导致不必要的计算。
&& 逻辑与运算符:
如果左边的表达式为 false,则右边的表达式不会被计算,因为整个逻辑表达式的结果已经可以确定为 false,从而避免了不必要的计算。
&& 是一个短路运算符,它在布尔逻辑中执行逻辑与操作,并且只在必要时计算右边的表达式。
10. int b = 5;
b = ((b-- > 3) && (–b < 3)) ? b++ : b–;
System.out.println(b);// 输出 3
1 int b = 5;:将变量 b 初始化为 5。
2 b = ((b-- > 3) && (–b < 3)) ? b++ : b–;:
首先,进行条件表达式的计算。
b-- > 3:这是一个条件判断,检查 b 是否大于 3。因为 b 是 5,所以这个条件为 true。然后 b 的值会先被使用,变为 4。
–b < 3:这是另一个条件判断,检查 b 自减后是否小于 3。由于 b 在上一个条件判断中变为 4,自减后变为 3,所以这个条件为 false。
((true) && (false)):由于两个条件判断的结果是 true 和 false,所以整个条件表达式的结果为 false。
因此,执行 b–,b 的值减少为 3。
System.out.println(b);:打印变量 b 的值,即 3。
11 java 有匿名类 匿名方法
匿名类:
在 Java 中,你可以创建一个没有显式命名的类,称为匿名类。匿名类通常用于实现接口或扩展类,而无需为其创建专门的类文件
public class AnonymousClassExample {
public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Thread is running.");
}
});
thread.start();
}
}
匿名方法(匿名函数):
方法包含于类或对象中;
方法在程序中被创建,在其他地方被引用。
设计方法的原则:方法的本意是功能块,就是实现某个功能的语句块的集合。我们设计方法的时候,最好保持方法的原子性,就是一个方法只完成1个功能,这样利于我们后期的扩展。
Java 8 引入了 Lambda 表达式,它是一种匿名方法(匿名函数)的形式。Lambda 表达式允许你以更紧凑的方式定义匿名函数,并可以在函数式接口(只有一个抽象方法的接口)中使用。
示例:使用 Lambda 表达式实现一个简单的接口。
interface MathOperation {
int operate(int a, int b);
}
public class AnonymousMethodExample {
public static void main(String[] args) {
MathOperation addition = (a, b) -> a + b;
System.out.println("Result: " + addition.operate(5, 3));
}
}
在这个示例中,我们通过 Lambda 表达式实现了一个函数式接口 MathOperation,定义了一个匿名方法来执行加法操作。
匿名类和匿名方法都使代码更加简洁,并且在某些情况下可以提高代码的可读性和可维护性。
12 方法必须有方法名吗?
大多数情况下,函数或方法需要一个方法名:
在大多数编程语言中,函数或方法需要一个独特的方法名来标识它们。方法名用于在代码中引用和调用该函数或方法,以便执行其功能。方法名的存在使得代码的可读性和可维护性更高,因为其他开发人员可以通过方法名理解函数的用途和功能。
匿名函数(Lambda 表达式):
然而,一些现代编程语言(例如 Java 8+、Python、JavaScript 等)引入了匿名函数的概念,也称为 Lambda 表达式。匿名函数是一种没有显式方法名的函数,通常用于在函数式编程范式中传递行为。
在匿名函数中,没有单独的方法名,而是通过函数参数传递行为。这种方式使代码更具有灵活性,可以将函数作为参数传递给其他函数或方法。
示例:Java 中的 Lambda 表达式
interface MathOperation {
int operate(int a, int b);
}
public class LambdaExample {
public static void main(String[] args) {
MathOperation addition = (a, b) -> a + b;
int result = addition.operate(5, 3);
System.out.println("Result: " + result);
}
}
13 方法可以没有方法体
在一些特殊情况下的方法,它们可能没有方法体或只包含方法的声明。这通常用于抽象类、接口、抽象方法等概念中。让我为你解释一下:
抽象方法:
在面向对象编程中,抽象方法是一种没有具体实现的方法,它只有方法的声明,而没有方法体。抽象方法必须位于抽象类或接口中,它是一种为了让子类或实现类来实现的方法。
在 Java 中,抽象方法用 abstract 关键字声明,示例如下:
abstract class Shape {
abstract void draw(); // 抽象方法声明,没有方法体
}
class Circle extends Shape {
@Override
void draw() {
System.out.println("Drawing a circle");
}
}
public class AbstractMethodExample {
public static void main(String[] args) {
Circle circle = new Circle();
circle.draw();
}
}
在这个示例中,Shape 类中的 draw 方法是一个抽象方法,它只有方法声明,而没有具体的方法体。子类 Circle 继承了 Shape 并实现了 draw 方法的具体实现。
接口中的方法:
在 Java 中,接口是一种特殊的引用类型,它只包含抽象方法的声明,而没有实现。所有接口中的方法都是隐式地抽象的,不需要加上 abstract 关键字。
示例:
interface MathOperation {
int operate(int a, int b); // 接口中的抽象方法,没有方法体
}
public class InterfaceExample {
public static void main(String[] args) {
MathOperation addition = (a, b) -> a + b;
int result = addition.operate(5, 3);
System.out.println("Result: " + result);
}
}
在这个示例中,MathOperation 接口中的 operate 方法是一个抽象方法,只有方法声明,没有具体实现。
所以,在特定的编程概念中,的确存在方法没有方法体的情况,这使得这些概念更具有灵活性和可扩展性。
14-1 方法的重载
重载就是在一个类中,有相同的函数名称,但形参不同(数量、质量 )的函数。
应用:类中构造器的重载!用于类的初始化
在你的代码中,定义了三个类:SuperType、SubType 和 Main。SuperType 是基类,SubType 是派生类,而 Main 是包含 main 方法的类。下面来解释一下这些类的行为和输出。
public class SuperType {
public SuperType() {
System.out.print("super");
}
}
public class SubType extends SuperType {
public SubType() {
System.out.print("sub");
}
}
public class Main {
public static void main(String[] args) {
SubType st = new SubType();
}
}
//supersub
// 定义基类 SuperType
public class SuperType {
// 基类的构造函数
public SuperType() {
// 输出 "super"
System.out.print("super");
}
}
// 定义派生类 SubType,继承自 SuperType
public class SubType extends SuperType {
// 派生类的构造函数
public SubType() {
// 输出 "sub"
System.out.print("sub");
}
}
// 定义包含 main 方法的类 Main
public class Main {
// 主方法
public static void main(String[] args) {
// 创建 SubType 类的实例 st
SubType st = new SubType();
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DtJ4NoEe-1691948389380)(media/16917251481696/16918885095646.jpg)]
在这份代码中,定义了三个类:SuperType、SubType 和 Main。SuperType 是基类,SubType 是派生类,Main 包含 main 方法。
public class SuperType {:定义基类 SuperType。
public SuperType() {:基类的构造函数,它在对象被创建时自动调用
System.out.print(“super”);:输出 “super”。
public class SubType extends SuperType {:定义派生类 SubType,它继承自 SuperType。
public SubType() {:派生类的构造函数。
System.out.print(“sub”);:输出 “sub”。
public class Main {:定义包含 main 方法的类 Main。
public static void main(String[] args) {:main 方法,程序的入口。
SubType st = new SubType();:创建 SubType 类的实例 st,这会触发构造函数的调用。
在这个代码中,当你创建一个 SubType 的实例时,会触发构造函数的调用。
首先,基类的构造函数 SuperType() 被调用,
然后派生类的构造函数 SubType() 被调用。
14-2 在创建子类的实例时,构造器会按照继承链的顺序被逐个调用。
当创建子类的实例时,会自动调用父类的构造器和子类的构造器。
这是因为子类继承了父类的属性和方法,包括构造器。
在创建子类的实例时,构造器会按照继承链的顺序被逐个调用。
public class Parent {
public Parent() {
System.out.println("Parent constructor");
}
}
public class Child extends Parent {
public Child() {
System.out.println("Child constructor");
}
}
public class Main {
public static void main(String[] args) {
Child child = new Child();
}
}
在这个例子中,当你创建 Child 类的实例 child 时,会自动调用父类 Parent 的构造器,然后再调用子类 Child 的构造器。运行这个程序,你会看到输出:
Parent constructor
Child constructor
这表明构造器的调用顺序是先调用父类的构造器,然后再调用子类的构造器。这确保了在创建子类的实例时,父类的初始化代码也能得到执行。
需要注意的是,如果你没有显式地在子类的构造器中调用父类的构造器,Java 会默认调用父类的无参构造器(如果父类有无参构造器)。如果父类没有无参构造器,并且你没有在子类构造器中显式地调用父类构造器,那么会编译错误。
14-3 显式地调用父类构造器
当你在子类的构造器中显式地调用父类的构造器时,你可以使用关键字 super 来实现。使用 super 关键字,你可以在子类的构造器中调用父类的构造器。这通常用于在子类构造器中执行父类的初始化工作。
以下是一个示例,展示了如何使用 super 关键字显式地调用父类的构造器:
public class Parent {
public Parent() {
System.out.println("Parent constructor");
}
}
public class Child extends Parent {
public Child() {
super(); // 调用父类的构造器
System.out.println("Child constructor");
}
}
public class Main {
public static void main(String[] args) {
Child child = new Child();
}
}
Parent constructor
Child constructor
这表明在子类构造器中使用 super() 调用父类构造器时,先执行父类构造器的初始化代码,然后再执行子类构造器的初始化代码。
需要注意的是,如果你在子类的构造器中没有显式地使用 super() 调用父类构造器,Java 会自动隐式地调用父类的无参构造器(如果存在)。如果父类没有无参构造器,你需要在子类构造器中显式地调用父类的某个有参构造器。
让我用一个简单的示例来说明这个过程:
运行 Main 类的 main 方法,输出将是:
supersub
解释:
当你创建 SubType 的实例时,它的构造函数 SubType() 被调用。
在 SubType 的构造函数中,会先调用基类 SuperType 的构造函数 SuperType(),输出 “super”。
接着,派生类 SubType 的构造函数继续执行,输出 “sub”。
所以,最终的输出是 “super” 和 “sub”,合并成了 “supersub”。
构造函数(Constructor)是一种特殊类型的方法,在创建一个类的实例(对象)时被调用。它主要用于初始化对象的状态和属性。构造函数具有与类同名的名称,并且没有显式的返回类型。在 Java 中,构造函数的主要特点包括:
- 与类同名: 构造函数的名称必须与所在类的名称完全一致。
- 没有返回类型: 构造函数没有返回类型,包括 void,因为它们的主要目的是创建对象而不是返回值。
- 自动调用: 构造函数在创建对象时自动调用。当使用 new 关键字创建类的新实例时,相应的构造函数会被调用。
- 可以重载: 与其他方法一样,构造函数也可以被重载,即在同一个类中可以定义多个不同参数列表的构造函数。
- 隐式默认构造函数: 如果没有显式地为类定义构造函数,Java 会提供一个默认的无参构造函数,它不执行任何特定的初始化操作。但是,如果你定义了任何构造函数,包括有参构造函数,那么默认的无参构造函数就不会再自动生成。
例如,以下是一个简单的类和构造函数的示例:
构造器说白了就是一个方法,所以它和普通的方法一样也可以做方法重载,换句话说就是构造器的重载。和方法重载一样构造器重载就是多个一样名字参数类型和参数的个数不同的多个构造器。构造器也叫构造方法(constructor), 用于对象初始化. 构造器是一个创建对象时被自动创建的特殊方法,目的是对象的初始化.
class Person {
String name;
int age;
// 无参构造器
public Person() {
name = "Unknown";
age = 0;
}
// 带参数构造器
public Person(String n, int a) {
name = n;
age = a;
}
// 方法用于输出信息
public void displayInfo() {
System.out.println("Name: " + name + ", Age: " + age);
}
}
public class ConstructorExample {
public static void main(String[] args) {
Person person1 = new Person(); // 使用无参构造器
person1.displayInfo(); // 输出: Name: Unknown, Age: 0
Person person2 = new Person("Alice", 25); // 使用带参数构造器
person2.displayInfo(); // 输出: Name: Alice, Age: 25
}
}
再来一个示例:
// 定义基类 Animal
public class Animal {
public int age; // 定义属性 age,用于存储年龄
// 基类的有参构造函数,接受一个 age 参数
public Animal(int age) {
this.age = age; // 将传入的 age 参数赋值给属性
}
// 基类的无参构造函数
public Animal() {
}
}
// 定义派生类 Person,继承自 Animal
public class Person extends Animal {
// 派生类的构造函数,接受一个 age 参数
public Person(int age) {
super(age); // 调用父类的有参构造函数,传递 age 参数到 Animal 的构造函数
}
}
// 主类 Main
public class Main {
public static void main(String[] args) {
Person p = new Person(10); // 创建一个 Person 对象,传入年龄 10
System.out.println(p.age); // 输出对象的年龄属性值,应该是 10
}
}
//输出 0
核心:这里 Person(10) 并未传到Animal中去。
14-4 从子类传参数到父类: super(age);
super(age); // 调用父类的有参构造函数,传递 age 参数
通过 super(age) 调用了父类 Animal 的有参构造函数,将传入的 age 参数正确地传递到了基类的构造函数中,从而初始化了 age 属性。
尽量用简单的语言解释了每行代码的作用:
-
定义基类 Animal。
-
定义一个整数类型的属性 age 用于存储年龄。
-
基类的有参构造函数,接受一个 age 参数,并将传入的值赋给属性。
-
基类的无参构造函数。
-
定义派生类 Person,继承自 Animal。
-
派生类的构造函数,接受一个 age 参数。
-
在派生类的构造函数中,使用 super() 调用父类的无参构造函数(默认调用)。
-
主类 Main。
-
主方法 main。
-
创建一个 Person 对象 p,传入年龄值 10。
-
输出 p 对象的 age 属性值,默认为 0。
-
最终输出结果为 0,因为在 Person 类的构造函数中,虽然调用了父类的构造函数,但没有显式地传递传入的 age 参数,所以父类的构造函数会将 age 设置为默认值 0。
15 方法的声明不能嵌套!!方法中不能在声明方法
在大多数编程语言中,包括 Java,方法(函数)的声明不能嵌套在其他方法内部,也不能在一个方法中再次声明另一个方法。方法应该在类的作用域中进行声明,而不是在其他方法的内部。
这是因为方法的声明和定义通常用于在类级别上定义一种行为,而不是在方法内部定义新的方法。在方法内部声明方法会导致代码结构混乱,并且可读性会大大降低。
以下是一个示例,展示了在 Java 中不允许在方法内部嵌套声明方法:
public class NestedMethodExample {
public void outerMethod() {
// 这里是外部方法的代码
// 不能在外部方法内部声明新的方法
// 以下是错误的示例
/*
public void innerMethod() {
// 这里是内部方法的代码
}
*/
}
public static void main(String[] args) {
NestedMethodExample example = new NestedMethodExample();
example.outerMethod();
}
}
插播复习方法的定义:
修饰符 返回值类型 方法名(参数类型 参数名){
…
方法体
…
return 返回值;
}
调用方法:对象名.方法名(实参列表)
16 雕虫小技 bug富含蛋白质 奇文共赏
来看一段错误代码:
package run;
public class Main {
public static void main(String[] args) {
int result = add(10,20);
System.out.println(result);
}
public static void add(int num1,int num2){
int result=num1 + num2;
}
}
直接编译不通过。
原因:
add 方法中计算的 result 变量没有返回值,因此在 main 方法中的 result 变量没有得到正确的值。
要使代码正确运行,需要在 add 方法中返回计算结果。
需要1 修改 void 为 int 返回int类型的值
2 return result;
以下是修正后的代码:
public class Main {
public static void main(String[] args) {
int result = add(10, 20);
System.out.println(result);
}
public static int add(int num1, int num2) {
int result = num1 + num2;
return result; // 返回计算结果
}
}
17 return new Integer(a+b);
public class Main {
public static void main(String[] args) {
int a = 20;
String b = "10";
Integer result = Integer.valueOf(a + b);
System.out.println(result); // 输出:2010
}
}
是一个用于创建一个新的 Integer 对象,并将两个整数相加后的结果作为其值的操作。这种操作在一些特定情况下是有用的,但需要注意的是,从 Java 9 开始,这种用法不再推荐使用。
public class IntegerExample {
public static void main(String[] args) {
Integer num1 = 10;
Integer num2 = 10;
System.out.println(num1 == num2); // 输出 true,因为在范围内的整数值会共享对象
}
}
18 花式重载
package com.zhangyy.opp;
import java.util.Scanner;
public class Demo01 {
public static void main(String[] args) {
int a = 20;
String b = "10";
int c = method1(a, b);
System.out.println(c);
System.out.println(method1(b,c));
System.out.println(method1(b,method1(a,b)));;
}
public static int method1(int a,String b){
return new Integer(a+b);
}
public static String method1(String a,int b){
return b+a;
}
}
/*
2010
201010
201010
*/
19 package关键字放在import关键字前面
在 Java 代码中,package 关键字应该放在 import 关键字之前。package 用于定义代码的包名,而 import 用于导入其他包中的类或接口,因此它们在代码的结构中是有顺序的。
正确的代码结构应该是这样的:
package com.example.mypackage; // 包名放在最前面
import java.util.ArrayList; // 导入语句紧随其后
public class MyClass {
// 类的定义部分
}
20 函数的化简顺序是什么
函数的化简顺序通常遵循以下两个基本原则:从内到外,从左到右。这两个原则有助于使代码更加清晰、模块化和易于阅读。让我为你解释一下这两个原则的含义:
从内到外:
这个原则表示,在一个函数调用链中,首先处理内部的函数调用,然后将结果传递给外部的函数调用。这种方式确保中间步骤的结果可以无缝地传递给后续步骤,从而形成一个完整的处理过程。这有助于保持代码的一致性,同时也使代码的逻辑更容易理解。
从左到右:
这个原则表示,在一个函数调用链中,函数的调用顺序是从左到右的。也就是说,先调用最左边的函数,然后将其结果传递给下一个函数,以此类推,最终得到最右边函数的结果。这样的顺序让代码更加具有逻辑性,让人能够更容易地跟随代码的执行过程。
这两个原则的应用可以使代码更加紧凑、易于维护和易于理解。它们是函数式编程的核心思想,被广泛应用于一些函数式编程语言和范式中,如 Haskell、Scala、Clojure 等。
21 main方法 头
main() 方法的头定义可以根据情况任意更改。虽然 main() 方法的标准定义是 public static void main(String[] args),但在 Java 5 之后,方法的参数名称 args 可以更改为其他有效的标识符,只要保持参数类型和顺序不变。
一个类可以没有 main() 方法。main() 方法是 Java 程序的入口点,但不是所有的类都需要定义 main() 方法。只有作为程序入口的类需要定义 main() 方法。
main() 方法并不一定需要放在公共类中。虽然 main() 方法通常在公共类中,但这不是必须的。main() 方法可以在非公共类中定义,只要程序的入口点位于公共类中即可。
不是所有对象的创建都必须放在 main() 方法中。main() 方法通常用于启动程序,并在程序中创建所需的对象。但是,并不是所有的对象都必须在 main() 方法中创建。对象可以在程序的其他地方创建,取决于程序的结构和逻辑。
22 用字符串 示例 == 严 引用对象内存地址 和equals 内容即可
public class StringComparisonExample {
public static void main(String[] args) {
String str1 = new String("hello");
String str2 = new String("hello");
String str3 = str1; // 指向同一个对象
System.out.println("Using ==:");
System.out.println(str1 == str2); // 输出 false,因为它们引用的是不同的对象
System.out.println(str1 == str3); // 输出 true,因为它们引用的是同一个对象
System.out.println("\nUsing equals:");
System.out.println(str1.equals(str2)); // 输出 true,因为 equals 比较的是内容
System.out.println(str1.equals(str3)); // 输出 true,因为 equals 比较的是内容
}
}
23 Java中
1 引用类型初始化后未赋值之前的值为null
2 基本数据类型
byte、short、int、long、boolean、char、float、double
这里要注意char类型初始化之后的默认值为空白null(注意:不是空格)
类型 | 初始化后的默认值 |
---|---|
boolean | false |
char | ‘/uoooo’(null) |
byte | (byte)0 |
short | (short)0 |
int | 0 |
long | 0L |
float | 0.0f |
double | 0.0d |
import java.util.Arrays;
public class Test {
public static byte byte1;
public static short s;
public static int i;
public static long l;
public static char c;
public static boolean boolean1;
public static float f;
public static double d;
@org.junit.Test
public void test() throws Exception {
System.out.println("byte:" + byte1); //byte:0
System.out.println("short:" + s); //s:0
System.out.println("int:" + i); //i:0
System.out.println("long:" + l); //l:0
System.out.println("char:" + c); //char:
System.out.println("boolean:" + boolean1); //boolean:false
System.out.println("float:" + f); //l:0.0
System.out.println("double:" + d); //l:0.0
}
}
这里要注意char类型初始化之后的默认值为空白null(注意:不是空格)
24 接23 空字符串:
空字符串是一个长度为 0 的字符串,表示没有字符的字符串。在 Java 中,可以使用两种方式表示空字符串:
1 通过字面值:String emptyString = “”;
2 使用 String 类的构造方法:String emptyString = new String();
String emptyString1 = ""; // 通过字面值创建空字符串
String emptyString2 = new String(); // 使用构造方法创建空字符串
System.out.println(emptyString1.isEmpty()); // 输出 true,因为长度为 0
System.out.println(emptyString2.isEmpty()); // 输出 true,因为长度为 0
import java.util.Scanner;
public class Demo01 {
public static void main(String[] args) {
String emptyString = new String(); // 使用构造方法创建空字符串
System.out.println(emptyString);
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mzyhOXaA-1691948389381)(media/16917251481696/16918305246084.jpg)]
25 class Person{ public Dog pet = new Dog(); }默认每人都有狗
class Person {
public int age;
public String name;
public Dog pet = new Dog(); // 默认的宠物狗
// 其他类成员和方法
}
定义了一个名为 Person 的类,该类有以下成员:
public int age;:这是一个公共成员变量,表示人的年龄,可以在类的外部访问和修改。
public String name;:这是一个公共成员变量,表示人的姓名,也可以在类的外部访问和修改。
public Dog pet = new Dog();:这是一个公共成员变量,表示人的宠物。它使用 new Dog() 初始化为一个 Dog 对象,意味着每个 Person 对象都会有一个默认的宠物狗。
法外张三 和 旺财的快乐生活:
package com.zhangyy.opp;
// Dog 类表示宠物狗
class Dog {
public String name;
// 构造方法,用于初始化宠物狗的名字
public Dog() {
this.name = "旺财"; // 默认名字为“旺财”
}
// 狗叫的方法
public void bark() {
System.out.println(name + ":汪汪汪!");
}
}
// Person 类表示人
class Person {
public int age;
public String name;
public Dog pet = new Dog(); // 默认的宠物狗
// 构造方法,用于初始化人的年龄和名字
public Person(int age, String name) {
this.age = age;
this.name = name;
}
// 与宠物狗一起玩耍的方法
public void playWithPet() {
System.out.println(name + " 和 " + pet.name + " 正在一起玩耍!");
pet.bark();
}
// 自我介绍的方法
public void introduce() {
System.out.println("我是 " + name + ",今年 " + age + " 岁,我有一只叫做 " + pet.name + " 的宠物狗。");
}
}
// 主类 HappyLifeScript
public class HappySugerLife {
public static void main(String[] args) {
// 创建一个名为“张三”的人,并设置年龄为 27
Person person = new Person(27, "张三");
// 调用自我介绍方法
person.introduce();
// 调用与宠物狗一起玩耍的方法
person.playWithPet();
}
}
25 装箱(Boxing)和拆箱(Unboxing)
装箱(Boxing)和拆箱(Unboxing)是 Java 中用于基本数据类型和对应的包装类之间转换的过程。
装箱(Boxing):
装箱是将基本数据类型转换为对应的包装类的过程。在装箱过程中,基本数据类型的值被封装到包装类的实例中。Java 提供了自动装箱和显式装箱两种方式。
自动装箱示例:
int intValue = 42;
Integer integerValue = intValue; // 自动装箱
显式装箱示例:
int intValue = 42;
Integer integerValue = Integer.valueOf(intValue); // 显式装箱
拆箱(Unboxing):
拆箱是将包装类中封装的基本数据类型值提取出来的过程。在拆箱过程中,包装类的实例被转换为基本数据类型的值。Java 提供了自动拆箱和显式拆箱两种方式。
自动拆箱示例:
Integer integerValue = 42;
int intValue = integerValue; // 自动拆箱
显式拆箱示例:
Integer integerValue = Integer.valueOf(42);
int intValue = integerValue.intValue(); // 显式拆箱
26 方法中参数声明父类时,可以传入子类对象,被称为"多态性"。
是的,这是面向对象编程中的一个重要特性,被称为"多态性"。
在 Java 中,可以将一个子类的实例传递给一个方法,该方法的参数类型声明为其父类类型。这个特性允许你在不知道具体子类类型的情况下操作对象,从而增加了代码的灵活性和可扩展性。
这种行为基于继承关系,允许你在一个通用的父类上进行操作,而无需关心实际传递的是哪个子类。这是通过多态性实现的,多态性是面向对象编程的核心概念之一。
例如,假设你有一个基类 Animal 和两个子类 Dog 和 Cat:
class Animal { }
class Dog extends Animal { }
class Cat extends Animal { }
你可以这样调用一个方法并传递不同子类的实例:
public void processAnimal(Animal animal) {
// 在这里操作 Animal 类型的对象
}
public static void main(String[] args) {
Animal dog = new Dog();
Animal cat = new Cat();
processAnimal(dog); // 可以传入 Dog 类型的对象
processAnimal(cat); // 可以传入 Cat 类型的对象
}
27 java.lang和java.util包
这两个包,java.lang和java.util,是Java编程语言的核心包之一,它们包含了许多常用的类和工具,用于支持各种基本的编程任务和操作。
java.lang包:
这是Java语言的核心包,其中包含了一些基本的类和对象,它们在Java程序中无需显式导入,因为它们在每个Java程序中都是默认可用的。这些类包括:
Object:所有类的超类,提供了一些基本方法如equals()、hashCode()和toString()。
String:用于操作字符串的类,提供了丰富的字符串操作方法。
System:提供与系统相关的方法和字段,如标准输入输出流等。
Math:提供基本的数学运算方法,如平方根、三角函数等。
Integer、Double、Boolean 等基本数据类型的包装类。
Thread:用于多线程编程的类,包括线程的创建、同步等操作。
java.util包:
这个包包含了一些常用的工具类,用于处理集合、日期、时间、随机数等。一些重要的类和接口包括:
ArrayList、LinkedList:可变大小的数组和链表实现的集合类。
HashMap、LinkedHashMap、TreeMap:用于存储键值对的映射表实现。
HashSet、LinkedHashSet、TreeSet:用于存储不重复元素的集合实现。
Date、Calendar:用于处理日期和时间。
Random:生成随机数的类。
Scanner:用于从标准输入读取数据的类。
这些包提供了Java编程中常用的核心功能。如果你想要在代码中使用这些包中的类和方法,只需在代码的开头使用import语句导入所需的类即可,例如:
import java.util.ArrayList;
import java.util.HashMap;
public class MyClass {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
HashMap<String, Integer> map = new HashMap<>();
// 使用list和map进行操作
}
}
28 File类 (java.io.File):
File类在java.io包下面
FileInputStream类在java.io包下面
File类和FileInputStream类都是位于Java编程语言的java.io包下面的核心类,用于文件和输入流的操作。
File类 (java.io.File):
File类用于表示文件或目录的抽象路径名,它提供了一些方法用于操作文件和目录的属性、路径等。通过File类,您可以创建、删除、重命名文件或目录,查询路径信息等。以下是一些File类的常见用法:
import java.io.File;
public class FileExample {
public static void main(String[] args) {
// 创建File对象表示文件或目录
File file = new File("example.txt");
// 查询文件或目录的属性
System.out.println("Is file exists: " + file.exists());
System.out.println("Is it a directory: " + file.isDirectory());
System.out.println("Absolute path: " + file.getAbsolutePath());
// 创建新目录
File directory = new File("myDirectory");
directory.mkdir();
// 删除文件或目录
file.delete();
}
}
FileInputStream类 (java.io.FileInputStream):
FileInputStream类用于从文件中读取字节数据,它是输入流的一种。您可以使用FileInputStream来打开文件并逐个字节地读取其中的内容。以下是一个简单的示例:
import java.io.FileInputStream;
import java.io.IOException;
public class FileInputStreamExample {
public static void main(String[] args) {
try (FileInputStream fis = new FileInputStream("example.txt")) {
int byteRead;
while ((byteRead = fis.read()) != -1) {
System.out.print((char) byteRead); // 将字节转换为字符输出
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
需要注意的是,在使用这些类时,应该处理可能抛出的异常,如IOException。另外,在结束使用后,应该关闭文件流以释放资源,这在上面的示例中通过try-with-resources语句实现。
29 包 package
a编译通过:
package aa;
public class A { }
package aa.bb;
import aa.A;
public class B {
public A a = new A();
}
package bb;
import aa.bb.B;
public class C {
public B b = new B();
}
- 每个类都在独立的源文件中,没有多个包声明或类定义在同一个源文件中。
- 在类 B 中,通过 import aa.A; 导入了 A 类,因为它们在不同的包中,但是代码仍然是合法的。
- 类 C 导入了类 B 并创建了 B 的实例,也没有问题。
b编译不通过
package aa;
public class A { }
package aa.bb;
public class B {
public A a = new A();
}
package bb;
import aa.bb.B;
public class C {
public B b = new B();
}
30 如何避免 import 与类的使用上,编译错误
避免import语句与类的使用上的编译错误需要遵循一些Java的组织和语法规则。下面是一些方法来确保正确使用import语句和类,从而避免编译错误:
- 正确的包声明: 确保每个源文件的包声明与其所在的文件夹结构匹配。包声明在源文件的开头使用package关键字指定。例如,如果文件位于包com.example下,那么包声明应该是package com.example;。
- 正确的类定义: 确保每个源文件中只有一个公共类,并且类的名称与文件名相匹配。公共类的名称应该与文件名完全一致。
- 使用完整的类名: 如果您在一个类中使用不在同一个包中的类,可以使用完整的类名(包括包名)。这样,您不必使用import语句。例如,java.util.ArrayList而不是import java.util.ArrayList;。
- 只导入所需的类: 只导入您实际需要的类,而不是导入整个包。这可以避免可能的命名冲突。
- 使用IDE的自动导入功能: 集成开发环境(IDE)通常会自动为您添加import语句,确保您在使用类时能够正确地导入。IDE还可以帮助您查找和修复可能的导入错误。
- 遵循命名规范: 命名规范有助于避免名称冲突。按照Java的命名约定来为包、类和变量选择有意义的名称。
- 检查错误消息: 编译器会生成错误和警告消息,指出您的代码中的问题。仔细阅读这些消息,以便能够识别和修复问题。
- 检查包路径: 当您使用不在同一包中的类时,确保包路径是正确的。如果包路径不正确,编译器将无法找到类。
检查依赖关系: 如果类依赖于其他类,确保这些依赖项也正确地导入并在路径中可用。
通过遵循这些准则,您可以减少导入和类使用方面的编译错误,并且更好地组织您的Java代码。
用简单的代码示例来解释。假设我们有两个包:com.example 和 com.example.animals,每个包下都有一个类。
- 示例:正确使用import语句和类。
假设我们有以下文件结构:
markdown
Copy code
com
└── example
└── MyClass.java
└── animals
└── Dog.java
现在,让我们看一下如何在 MyClass.java 中正确使用 import 语句和类:
// MyClass.java
package com.example;
import com.example.animals.Dog;
public class MyClass {
public static void main(String[] args) {
Dog dog = new Dog();
dog.bark();
}
}
在这个示例中,我们在 MyClass.java 中使用了 import com.example.animals.Dog; 语句来导入 Dog 类,这样我们可以在 MyClass 中使用它,而不需要写完整的类路径。
- 示例:使用完整的类名。
如果您不想使用import语句,您可以直接使用完整的类名:
Copy code
// MyClass.java
package com.example;
public class MyClass {
public static void main(String[] args) {
com.example.animals.Dog dog = new com.example.animals.Dog();
dog.bark();
}
}
在这个示例中,我们没有使用 import 语句,而是直接在 MyClass 中使用了完整的类路径来创建 Dog 类的实例。
这两个示例演示了如何使用import语句和直接使用完整类名来避免编译错误。通过这些方法,您可以更好地组织代码,并确保它们在正确的包和类中运行。
default修饰符=>同包下的类可访问/继承,其他类可以继承
31 @Data注解. set get 方法 lombok包
可以自动生成该类私有属性的set get 方法
使用@Data注解,应导入lombok包
@Data 注解是 Lombok 提供的一个注解,用于自动生成 Java 类的一些标准方法,如 toString()、equals()、hashCode(),以及私有属性的 get 和 set 方法。这大大减少了重复编写这些标准方法的工作。同时,@Data 也包括 @Getter、@Setter、@ToString 和 @EqualsAndHashCode 的功能。
关于 @Data 注解的使用和相关导入,请参考以下几点:
导入 Lombok 包:
在使用 @Data 注解之前,确保您已经导入了 Lombok 包,以便编译器能够识别和处理注解。您可以在项目的构建工具中添加 Lombok 依赖,比如 Maven 或 Gradle。
自动生成 get 和 set 方法:
当您在类上使用 @Data 注解时,它将自动为类中的所有私有字段生成 get 和 set 方法。这样,您不需要显式编写这些方法。
自动导入外部类:
@Data 注解本身不会自动导入类里引用的外部类。Java 在导入外部类方面仍然遵循常规的导入规则。如果您在类中引用了外部类,您仍需要使用标准的 import 语句来导入这些外部类。
导入 java.util 包:
与 @Data 注解本身无关,java.util 包是 Java 标准库中的一个包,用于提供各种常用的工具类和集合类。您可以在使用 Java 标准库中的类时,根据需要导入 java.util 包。
综上所述,使用 @Data 注解可以让您轻松地生成标准方法和私有属性的 get 和 set 方法,但它不会自动导入外部类。同时,确保在使用 Lombok 注解之前导入了 Lombok 包。至于 java.util 包的导入,与 @Data 注解本身无关,而是与您在代码中使用的具体类有关。
public abstract class Person {}
32 抽象类不一定需要包含抽象方法.
抽象类:
抽象类是不能被实例化的类,它可以包含抽象方法和非抽象方法。
抽象方法是一种没有实现体的方法,它只有方法声明,没有方法体。
抽象类用于作为其他类的基类,它可以定义一些共享的属性和方法,但可能需要子类来实现具体的方法逻辑。
抽象方法:
抽象方法是在抽象类中声明的方法,但没有提供实际的方法实现。子类继承抽象类时,需要提供对抽象方法的具体实现。抽象方法通常用来定义一个接口要求继承的子类必须提供特定的方法实现。
所以,抽象类有两种类型的方法:
抽象方法: 这些方法没有实际的实现,需要由子类提供具体实现。
非抽象方法: 这些方法在抽象类中有实际的实现,子类可以直接继承或者覆盖。
以下是一个示例,展示了抽象类不一定拥有抽象方法:
Copy code
abstract class Animal {
String name;
// 非抽象方法
void eat() {
System.out.println(name + " is eating");
}
}
class Dog extends Animal {
Dog(String name) {
this.name = name;
}
}
class Cat extends Animal {
Cat(String name) {
this.name = name;
}
}
public class Main {
public static void main(String[] args) {
Dog dog = new Dog("Buddy");
dog.eat();
Cat cat = new Cat("Whiskers");
cat.eat();
}
}
在这个示例中,Animal 是一个抽象类,但它没有抽象方法,而是拥有一个非抽象方法 eat()。Dog 和 Cat 类继承自 Animal 类,可以直接继承并使用 eat() 方法。这个示例说明了抽象类不一定需要拥有抽象方法,它可以包含具体实现的非抽象方法。
抽象类的几个实现的例子:
分析您提供的三个代码示例:
public abstract class Animal {
public abstract void walk();
public void eat(){}
}
class Person extends Animal {
public void walk() {}
}
eat() 方法是一个非抽象方法,但它在 Animal 类中并没有给出具体的方法体。这在 Java 中是合法的,因为非抽象方法可以有默认的实现。如果在子类中没有覆盖这个方法,它将继续使用默认的空实现。在这个特定的示例中,Person 类并没有为 eat() 方法提供覆盖,因此它继承了默认的空实现。
所以,eat() 方法在 Animal 类中的默认实现是一个空的方法体。这意味着如果在子类中没有覆盖这个方法,它会继续使用空的默认实现。
Copy code
public abstract class Animal {
public abstract void walk();
public void eat(){}
}
abstract class Person extends Animal {}
```继承了抽象类的抽象
在这个示例中,您同样定义了一个抽象类 Animal,其中包含一个抽象方法 walk() 和一个非抽象方法 eat()。然后,您定义了一个名为 Person 的抽象类**它继承自 Animal 类。因为 Person 类是抽象类,您不需要提供对 walk() 和 eat() 方法的具体实现。**
即示例中,Person 类是一个抽象类,继承自 Animal 类,而且不需要提供对 walk() 和 eat() 方法的具体实现,因为它是一个抽象类。
***
```java
public abstract class Animal {
public abstract void walk();
public void eat(){}
}
abstract class Person extends Animal {
public void walk(){}
}
在这个示例中,与第一个示例类似,您定义了一个抽象类 Animal,其中包含一个抽象方法 walk() 和一个非抽象方法 eat()。然后,您定义了一个名为 Person 的抽象类,它继承自 Animal 类,并实现了抽象方法 walk()。同样地,Person 类没有重写 eat() 方法。
Person 抽象类继承了 Animal 抽象类,并提供了对 walk() 方法的实现,虽然该实现{}为空。
这意味着继承自 Person 类的具体子类不再需要提供 walk() 方法的实现,因为 Person 抽象类已经提供了一个空的默认实现。这个特性使得继承层次中的具体子类可以选择性地覆盖 walk() 方法,或者直接使用来自 Person 类的默认实现。
总结:
在这三个示例中,您都展示了抽象类 Animal 和抽象类 Person 的关系。在每个示例中,Animal 类都包含一个抽象方法 walk() 和一个非抽象方法 eat()。Person 类继承自 Animal 类,并根据需要实现了抽象方法 walk()。Person 类可以是具体类,也可以是抽象类,取决于是否提供了对 walk() 方法的实际实现。
33 JAVA中接口中可以不定义方法,编译不报错; JAVA中接口中只能定义抽象方法。即 有{}方法体的 一律为错。
JAVA中接口中只能定义抽象方法