1.相对路径和绝对路径的区别:
绝对路径是指从根目录开始的完整路径
相对路径是相对于当前工作目录的路径,不是唯一的,而是根据当前位置而变化的,
相对路径是相对于java程序所在的目录,或者相对于java程序运行时的工作目录
2.代码启动时的运行顺序,过程
JVM启动,加载核心类库以及用户编写的类
执行main方法,开始运行程序
根据代码中的语法和逻辑,执行各种语句和表达式,包括创建对象、调用方法、访问变量等
当main方法执行完毕或者调用了System.exit方法时,程序结束,JVM终止
3.java的执行过程
先编译后执行,java首先通过编译器将源代码(java文件)编译成字节码文件(class文件),编译器将源代码转换为字节代码
java编译器(javac)
字节码文件,是谁执行的?
任何支持Java虚拟机(JVM)的平台上运行。
JVM是什么?跨不跨平台?
JVM是java虚拟机的缩写,是java程序的运行时环境,JVM可以在不同的操作系统和硬件平台上运行java程序,因此java程序具有很高的可移植性和跨平台性
4.如何在小黑屏(CMD)中打印"Hello, World!"可以通过以下步骤实现:
打开小黑屏(CMD),进入一个工作目录,例如"C:\Users\YourUsername"。
使用文本编辑器(例如Notepad)创建一个新的文本文件,将以下代码复制并粘贴到该文件中:
echo Hello, World!
将该文件保存为"HelloWorld.bat",注意文件后缀名为".bat"。
在小黑屏(CMD)中执行该批处理文件,可以通过以下两种方式:
双击该文件,在小黑屏(CMD)中执行。
在小黑屏(CMD)中切换到该文件所在的目录,输入文件名并按回车键执行。
程序将会在小黑屏(CMD)中打印出"Hello, World!"。
注意,批处理文件是一种批处理脚本,可以在Windows操作系统中运行。该方法只是一种简单的展示如何在小黑屏(CMD)中打印"Hello, World!"的方法,如果您需要在Java中打印"Hello, World!",则需要编写Java程序并使用Java编译器(javac)来编译该程序并在小黑屏(CMD)中运行。
程序开始的入口
在java中程序的入口是main方法,main方法是程序开始执行的地方,它是java程序的最初入口点.在java中,main方法的定义必须满足以下两个条件:
1.main方法必须是public和static
2.main方法的返回值类型必须是void,并接受一个String类型的数组作为参数
四类八种,取值范围
四类
基本数据类型:包括整型(int)、短整型(short)、长整型(long)、字节型(byte)、单精度浮点型(float)、双精度浮点型(double)、字符型(char)、布尔型(boolean)等。
引用数据类型:包括类(class)、接口(interface)、数组(array)、枚举(enum)等。
自定义数据类型:包括自定义类、接口、枚举等。
空类型(void):表示没有返回值的方法。
八种
四种基本数据类型:
byte < short < int < long < float < double < char < boolean
字节型(byte):8位有符号整数,取值范围为-128到127。 1个字节
短整型(short):16位有符号整数,取值范围为-32,768到32,767。 2个字节
整型(int):32位有符号整数,取值范围为-2,147,483,648到2,147,483,647。 4个字节
长整型(long):64位有符号整数,取值范围为-9,223,372,036,854,775,808到 4个字节9,223,372,036,854,775,807。
单精度浮点型(float):32位浮点数,取值范围为1.4E-45到3.4028235E38。 4个字节
双精度浮点型(double):64位浮点数,取值范围为4.9E-324到1.7976931348623157E308。 8个字节
字符型(char):16位Unicode字符,取值范围为0到65,535。 2个字节
布尔型(boolean):true或false。 1个字节
基本数据类型和引用数据类型的区别
存储位置:基本数据类型的值直接存储在内存中,而引用数据类型的值存储在堆(heap)中,它们的地址存储在栈(stack)中。
占用空间:基本数据类型占用的空间是固定的,而引用数据类型占用的空间大小取决于对象的大小。
默认值:基本数据类型有默认值,例如int类型的默认值是0,而引用数据类型的默认值是null。
参数传递方式:基本数据类型在方法调用时采用值传递方式,即将实际参数的值复制一份传递给形式参数,而引用数据类型在方法调用时采用引用传递方式,即将实际参数的地址传递给形式参数。
比较方式:基本数据类型比较值是否相等,而引用数据类型比较地址是否相等。如果需要比较引用数据类型的值是否相等,需要重写equals()方法。
整型和浮点型默认是哪种类型
整型和浮点型默认是int和double类型。
标识符命名规范
在Java中,标识符是指用来标识程序中各种变量、方法、类等的名称。标识符的命名规则如下:
标识符必须以字母、下划线(_)或美元符号($)开头,不能以数字开头。
标识符可以包含字母、数字、下划线(_)或美元符号($)。
标识符是区分大小写的。
标识符不能使用Java中的关键字和保留字。
需要注意的是,虽然Java中允许使用下划线(_)和美元符号($)作为标识符的一部分,但是在实际编程中,建议使用驼峰命名法来命名标识符,这样可以提高代码的可读性和可维护性。
大小驼峰命名法
通常使用小驼峰命名法来命名变量、方法、属性等成员变量,而使用大驼峰命名法来命名类和接口。
大驼峰命名法(也称为大写驼峰命名法或帕斯卡命名法)是一种命名规范,它主要用于命名类、接口、枚举等 Java 元素。具体来说,大驼峰命名法是指将多个单词组合起来构成一个复合词,其中每个单词的首字母都大写,以此类推。
以下是一些使用大驼峰命名法的示例:
类名:Person, Student, Book, CustomerOrder
接口名:Runnable, Comparable, Serializable
枚举名:DayOfWeek, Color, Size
小驼峰命名法(也称为小写驼峰命名法)是一种命名规范,它主要用于命名变量、方法、属性等 Java 元素。具体来说,小驼峰命名法是指将多个单词组合起来构成一个复合词,其中第一个单词的首字母小写,后面的每个单词的首字母大写,以此类推。
以下是一些使用小驼峰命名法的示例:
变量名:firstName, lastName, age, emailAddress
方法名:getFirstName(), setLastName(String lastName), calculateAge(int birthYear), sendEmail(String recipient, String subject, String body)
属性名:firstName, lastName, age, emailAddress
类,对象,包的命名
类的命名:类名应该以大写字母开头,采用大驼峰命名法,例如:Person、Student、Account。类名应该具有描述性,能够清晰地表达类的作用和含义。
对象的命名:对象的命名应该以小写字母开头,采用小驼峰命名法,例如:person、student、account。对象的命名应该具有描述性,能够清晰地表达对象的作用和含义。
包的命名:包名应该全部小写,采用点(.)分隔符分隔各级包名,包名一般采用公司或组织的域名倒序命名,例如:com.example、org.apache。如果是个人或者小团队开发的项目,也可以使用自己的域名作为包名的一部分 例如:io.github.username
包名的命名应该具有唯一性,能够清晰地表达包的作用和含义。
常量定义,规范
在Java中,常量是指在程序执行过程中其值不会发生改变的量。
常量名规范:
常量名应该全部大写,单词之间用下划线(_)分隔,例如:MAX_VALUE、PI。
常量名应该具有描述性,能够清晰地表达常量的含义和作用。
常量必须在定义时进行初始化,且初始化后不能再次修改其值。
常量可以定义在类中或者接口中,一般使用static final修饰。
转义符
转义符是一种特殊的字符序列,用来表示一些无法直接输入的字符或者在字符中表示的一些特殊的含义.
转义符以反斜杠(\)开头,后面跟着一个或多个字符。以下是常见的转义符:
\n:表示换行符
\t:表示制表符
\r:表示回车符
\b:表示退格符
\f:表示换页符
\’:表示单引号(’)
\”:表示双引号(")
\:表示反斜杠(\)
例如,在字符串中表示双引号(")可以使用转义符":
String message = "She said, \"Hello!\"";
需要注意的是,转义符也可以用于表示Unicode字符,例如:\u0020表示空格符。
转义符的使用可以使得Java程序更易于编写和阅读,但是也需要注意转义符的正确使用,避免出现编译错误和运行时错误。
数据类型转换关系
数据类型转换分为两种
1.隐式类型转换(自动类型转换)。
从小范围数据类型到大范围数据类型的转换,例如:
int a = 10;
double b = a; // int类型自动转换为double类型
2.显式类型转换(强制类型转换)。
显式类型转换:当需要将一个大范围数据类型转换为小范围数据类型时,需要进行强制类型转换。这种转换可能会导致精度损失或数据溢出,因此需要谨慎使用,例如:
ouble c = 3.14;
int d = (int)c; // double类型强制转换为int类型
需要注意的是,在数据类型转换时,应该避免数据溢出或失真的情况发生。在进行强制类型转换时,应该进行范围检查,以确保所转换的值在目标数据类型的取值范围内。
以下是Java中常用数据类型转换的关系:
byte -> short -> int -> long -> float -> double
char -> int
自动装箱:基本数据类型 -> 对应的包装类
自动拆箱:包装类 -> 对应的基本数据类型
在Java中,自动装箱(Autoboxing)是指将基本数据类型自动转换为对应的包装类的过程。自动装箱是Java语言的一个语法糖,使得程序员可以更方便地进行基本数据类型和包装类之间的转换。
例如,将一个int类型的值赋给Integer类型的变量时,可以直接进行赋值操作,无需手动创建Integer对象:
int a = 10;
Integer b = a; // 自动装箱,将int类型的值转换为Integer对象
自动装箱的过程是编译器自动进行的,其实质是将基本数据类型的值包装成对应的包装类对象。在自动装箱时,编译器会自动调用对应的包装类的valueOf()方法,例如:Integer.valueOf()、Double.valueOf()等。
需要注意的是,自动装箱和自动拆箱(Unboxing)是Java语言的两个特性,它们通常会共同出现。自动拆箱是指将包装类自动转换为对应的基本数据类型的过程。
例如,将一个Integer类型的值赋给int类型的变量时,可以直接进行赋值操作,无需手动调用intValue()方法:
Integer a = 10;
int b = a; // 自动拆箱,将Integer对象转换为int类型的值
自动装箱和自动拆箱可以使得程序员更加方便地进行基本数据类型和包装类之间的转换,同时也可以提高代码的可读性和可维护性。
字符串转整型的方法
Integer类(valueOf(String s))
在Java中,Integer是一个包装类(Wrapper Class),用于将int类型的基本数据类型封装成一个对象。Integer类提供了一些方法用于对整数进行操作和转换,例如:字符串转整型、整型转字符串、比较大小等。
Integer类是Java语言中常用的包装类之一,它继承自Number类,实现了Serializable和Comparable接口。在Java中,所有的包装类都提供了相应的方法来实现基本数据类型和包装类之间的转换,以方便程序员进行数据类型的转换和操作。
需要注意的是,由于Integer是一个对象类型,因此在进行操作时会产生额外的内存开销和对象创建的开销,相对于int类型的基本数据类型,效率会稍低。因此,在处理大量整数数据时,应该优先使用int类型的基本数据类型。
另外,需要注意的是,Integer类和int类型有一些重要的区别,例如:Integer类的对象可以为null,而int类型的变量不能为null。在进行操作时需要避免空指针异常的发生,同时也需要根据具体的需求选择合适的数据类型进行操作。
Integer.parseInt(String s):将字符串转换为int类型。例如:
String str = "123";
int num = Integer.parseInt(str);
Integer.valueOf(String s):将字符串转换为Integer类型。例如:
String str = "123";
Integer num = Integer.valueOf(str);
需要注意的是,以上两种方法在转换过程中,如果字符串不是合法的整数格式(例如包含非数字字符或超出整数范围等),会抛出NumberFormatException异常。因此,在进行字符串转换时,应该进行异常处理,以保证程序的稳定性。
同时,需要注意的是,以上方法只能将字符串转换为整数类型,如果需要将字符串转换为其他数据类型,需要使用对应的数据类型转换方法。例如,将字符串转换为浮点数类型可以使用Double.parseDouble()或Double.valueOf()方法,将字符串转换为长整型可以使用Long.parseLong()或Long.valueOf()方法等。
一元、二元、三元运算符
在Java中,根据操作数的数量,运算符可以分为一元、二元、三元运算符。
一元运算符:只需要一个操作数即可完成运算的运算符。常见的一元运算符有:
正负号运算符:+、-
自增自减运算符:++、--
逻辑非运算符:!
例如:
int a = 10;
int b = -a; // 负号运算符
int c = ++a; // 自增运算符
boolean d = !true; // 逻辑非运算符
二元运算符:需要两个操作数才能完成运算的运算符。常见的二元运算符有:
算术运算符:+、-、*、/、%
关系运算符:>、<、>=、<=、==、!=
逻辑运算符:&&、||、&
赋值运算符:=、+=、-=、*=、/=、%=
例如:
int a = 10;
int b = 20;
int c = a + b; // 算术运算符
boolean d = a > b; // 关系运算符
boolean e = true && false; // 逻辑运算符
a += 5; // 赋值运算符
三元运算符:需要三个操作数才能完成运算的运算符,也称为条件运算符(Conditional Operator)
它的语法格式为:
(条件表达式) ? 表达式1 : 表达式2
如果条件表达式为真,则执行表达式1,否则执行表达式2。
例如:
int a = 10;
int b = 20;
int max = (a > b) ? a : b; // 三元运算符
需要注意的是,在使用运算符时,需要遵循运算符的优先级和结合性规则,以便正确地进行运算和计算。
i++,++i的区别,运算符的优先级
i++和++i都是一元运算符,用于将变量i的值加1.它们的区别在于他们的运算顺序和返回值
i++是后缀递增运算符,它首先返回变量i的原始值,然后将变量i的值加1.(先用后加)
++i是前缀递增运算符,它首先将变量i的值加1,然后返回变量i的新值. (先加后用)
在表达式中使用这些运算符时,需要注意它们的优先级。++i的优先级高于i++,因此在表达式中使用时,需要根据需要使用括号来控制运算的顺序。
运算符的分类
圆括号(())
一元运算符(+、-、++、--、!、~)
乘除模运算符(*、/、%)
加减运算符(+、-)
移位运算符(<<、>>、>>>)
关系运算符(<、<=、>、>=、instanceof)
相等运算符(==、!=)
按位与运算符(&)
按位异或运算符(^)
按位或运算符(|)
逻辑与运算符(&&)
逻辑或运算符(||)
三元运算符(? :)
赋值运算符(=、+=、-=、*=、/=、%=、<<=、>>=、>>>=、&=、^=、|=)
位运算符(& | ^ ~ << >> >>> <<<)
常见的位运算符
按位与运算符(&):将两个操作数的每一个二进制位进行“与”操作,如果两个二进制位都为1,则结果为1,否则为0。
按位或运算符(|):将两个操作数的每一个二进制位进行“或”操作,如果两个二进制位都为0,则结果为0,否则为1。
按位异或运算符(^):将两个操作数的每一个二进制位进行“异或”操作,如果两个二进制位相同,则结果为0,否则为1。
按位取反运算符(~):将操作数的每一个二进制位进行取反操作,即0变成1,1变成0。
左移运算符(<<):将操作数的二进制位向左移动指定的位数,右侧用0填充。
右移运算符(>>):将操作数的二进制位向右移动指定的位数,左侧用符号位填充(即正数用0填充,负数用1填充)。
无符号右移运算符(>>>):将操作数的二进制位向右移动指定的位数,左侧用0填充。
位运算符只能用于整型数据类型,例如byte、short、int和long等。在使用位运算符时,需要注意运算符的优先级和结合性,以及数据类型的兼容性和运算顺序。
按位与(&)的例子:
int a = 5; // 二进制表示为 101
int b = 3; // 二进制表示为 011
int c = a & b; // 按位与运算,结果为 1(二进制表示为 001)
System.out.println(c); // 输出 1
按位或(|)的例子:
int a = 5; // 二进制表示为 101
int b = 3; // 二进制表示为 011
int c = a | b; // 按位或运算,结果为 7(二进制表示为 111)
System.out.println(c); // 输出 7
按位异或(^)的例子:
int a = 5; // 二进制表示为 101
int b = 3; // 二进制表示为 011
int c = a ^ b; // 按位异或运算,结果为 6(二进制表示为 110)
System.out.println(c); // 输出 6
按位取反(~)的例子:
int a = 5; // 二进制表示为 101
int b = ~a; // 按位取反运算,结果为 -6(二进制表示为 11111111 11111111 11111111 11111010)
System.out.println(b); // 输出 -6
左移(<<)的例子:
int a = 5; // 二进制表示为 101
int b = a << 2; // 左移 2 位,结果为 20(二进制表示为 10100)
System.out.println(b); // 输出 20
右移(>>)的例子:
int a = 5; // 二进制表示为 101
int b = a >> 2; // 右移 2 位,结果为 1(二进制表示为 1)
System.out.println(b); // 输出 1
以上是一些具体的位运算符的例子,它们可以用于各种计算任务,例如掩码操作、位操作、位移操作等。需要注意的是,在实际编程中,要确保对位运算符的正确理解和使用,以避免出现意外的错误。
StringBuilder和StringBuffer了解多少?和String的区别
StringBuilder和StringBuffer都是数组容器
StringBuilder只是一个容器,需要转化成字符串类型的
StringBuilder是一个创建好的类,打印的不是地址值,是自己里面改的,打印的是数据值
其中的方法:append(添加) reverse(反转) .length()获取长度
StringBuilder底层扩展:
默认创建一个长度为16的字节数组
添加的内容长队小于16,直接存
添加的内容大于16会扩容(原来的容量*2+2)
如果扩容之后还不够,以实际长度为准
StringBuilder和StringBuffer是Java中用于字符串拼接和操作的类,它们都继承自抽象类AbstractStringBuilder。StringBuilder是在Java 1.5中引入的,它与StringBuffer类似,但是StringBuilder的方法没有被synchronized修饰,因此在多线程环境下效率更高。
String和StringBuilder/StringBuffer的区别在于:
String是不可变的字符序列,一旦创建就不能被修改。因此,每次对String进行修改操作时,都会创建一个新的String对象,这可能会导致频繁的对象创建和销毁,从而降低程序的性能。而StringBuilder和StringBuffer都是可变的字符序列,可以在原对象的基础上进行修改操作,避免了频繁的对象创建和销毁,因此在对字符串进行频繁的修改操作时,使用StringBuilder和StringBuffer会更加高效。
注:StringBuilder底层其实是数组存储元素,链表处理扩容,并且是头插法
String、StringBuilder和StringBuffer都有各自的优缺点,需要根据实际的需求来选择合适的类。
如果需要对字符串进行频繁的修改操作,并且不需要考虑多线程同步问题,建议使用StringBuilder;
如果需要考虑多线程同步问题,可以使用StringBuffer;
如果字符串不需要修改,那么使用String即可。
三种定义数组的方法
.静态初始化方式:
使用静态初始化方式,可以在定义数组时直接为数组的每个元素赋值.具体语法如下:
数据类型[] 数组名 = {元素1, 元素2, ...};
int[] numbers = {1, 2, 3, 4, 5};
动态初始化方式:
使用动态初始化方式,可以在定义数组时指定数组的长度,并为数组分配内存空间。具体语法如下:
数据类型[] 数组名 = new 数据类型[数组长度];
int[] numbers = new int[5];
匿名数组方式:
使用匿名数组方式,可以创建一个没有名称的数组,并直接为数组的每个元素赋值。具体语法如下:
new 数据类型[]{元素1, 元素2, ...};
int[] numbers = new int[]{1, 2, 3, 4, 5};
需要注意的是,数组在定义时需要指定数据类型和数组的长度,数组的长度是固定的,一旦定义,就无法再改变。如果需要存储多个元素,并且元素的数量是可变的,可以考虑使用Java中的集合类。
写一个三维数组
以下是一个三维数组的完整代码示例:
public class Main {
public static void main(String[] args) {
// 定义一个 3 × 3 × 3 的三维数组
int[][][] array = new int[3][3][3];
// 初始化三维数组
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
for (int k = 0; k < 3; k++) {
array[i][j][k] = i + j + k;
}
}
}
// 遍历三维数组并输出其中的元素
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
for (int k = 0; k < 3; k++) {
System.out.print(array[i][j][k] + " ");
}
System.out.println();
}
System.out.println();
}
}
}
在上面的代码中,我们定义了一个 3 × 3 × 3 的三维数组,并使用三重循环初始化了其中的元素。然后,我们又使用三重循环遍历了整个三维数组,并输出其中的元素。
运行结果:
0 1 2
1 2 3
2 3 4
1 2 3
2 3 4
3 4 5
2 3 4
3 4 5
4 5 6
==和equals的区别
在Java中,==和equals()都是用于比较两个对象是否相等的操作符,但它们的比较方式有所不同。
==操作符用于比较两个对象的引用(内存地址)是否相等。如果两个对象的引用相同,则返回true,否则返回false。例如:
String str1 = "hello";
String str2 = "hello";
String str3 = new String("hello");
System.out.println(str1 == str2); // true,因为str1和str2引用的是同一个对象
System.out.println(str1 == str3); // false,因为str1和str3引用的是不同的对象
上面例子中为什么str1和str2引用的同一个对象?
在Java中,字符串常量池是一块特殊的内存区域,用于存储所有字符串常量。当程序中出现一个字符串常量时,JVM会首先检查字符串常量池中是否已经存在该字符串,如果存在,则直接返回该字符串的引用;如果不存在,则在字符串常量池中创建一个新的字符串,并返回该字符串的引用。这样,只要字符串的内容相同,不同的字符串变量在使用时都可以引用到同一个字符串对象。
在上面的例子中,str1和str2都是字符串常量,它们的值都为"hello",因此它们引用的是同一个字符串对象。而str3使用了new关键字创建了一个新的字符串对象,因此str1和str3引用的是不同的字符串对象。
equals()方法用于比较两个对象的内容是否相等。
默认情况下,equals()方法与==操作符的作用相同,即比较两个对象的引用是否相等。但是,对于一些类(如String、Integer等),它们重写了equals()方法,使其用于比较对象内容是否相等。例如:
String str1 = "hello";
String str2 = "hello";
String str3 = new String("hello");
System.out.println(str1.equals(str2)); // true,因为str1和str2的内容相同
System.out.println(str1.equals(str3)); // true,因为str1和str3的内容相同
需要注意的是,如果自定义类没有重写equals()方法,则默认使用父类Object的equals()方法,
面向对象的三大特性
封装、继承、多态
封装(Encapsulation):将数据和操作数据的方法(即行为)包装在一起,对外部隐藏对象的内部细节,只开放有限的接口供外部使用。封装可以提高程序的安全性和可靠性,降低程序的耦合度,方便代码重用和维护。在Java中,封装可以通过访问修饰符(public、private、protected等)来实现。
继承(Inheritance):通过继承,可以创建新的类,这些类具有父类的属性和方法,并可以添加自己的属性和方法。继承可以提高代码的复用性和可维护性,避免重复编写相似的代码。在Java中,继承可以通过extends关键字来实现,可以使用super关键字来调用父类的构造方法和方法。
多态(Polymorphism):指同一种行为具有多种不同的表现形式或状态的能力。在Java中,多态可以通过方法重载(Overloading)和方法重写(Overriding)来实现。
方法重载指在同一个类中定义多个同名但参数列表不同的方法,编译器根据参数列表的不同来决定调用哪个方法。
overload(重载)
1、参数类型、个数、顺序至少有一个不相同。
2、不能重载只有返回值不同的方法名。
3、存在于父类和子类、同类中。
方法重写指子类覆盖父类的同名方法,实现多态性。多态可以提高代码的灵活性和可扩展性,使代码更易于维护和拓展。
override(重写)
1、方法名、参数、返回值相同。
2、子类方法不能缩小父类方法的访问权限。
3、子类方法不能抛出比父类方法更多的异常(但子类方法可以不抛出异常)。
4、存在于父类和子类之间。
5、方法被定义为final不能被重写。
访问权限修饰符,权限范围。
在Java中,访问权限修饰符用于控制类、变量、方法等成员的可见性(即可以被哪些类或对象访问)。
Java中有四种访问权限修饰符:
public:公共的,可以被任何类或对象访问。使用public修饰的类、变量、方法等成员可以被其他类或对象访问。
protected:受保护的,可以被当前类、同一个包中的类或该类的子类访问。使用protected修饰的成员可以在当前类、同一个包中的类或该类的子类中访问,但不能在其他包中访问。
默认(即不使用任何修饰符):默认的,可以被当前类、同一个包中的类访问。使用默认访问权限修饰的成员可以在当前类、同一个包中的类中访问,但不能在其他包中访问。
private:私有的,只能被当前类访问。使用private修饰的成员只能在当前类中访问,其他类或对象不能访问。
需要注意的是,访问权限修饰符只能控制成员的可见性,而不能控制成员的访问方式(如读写权限等)。如果需要控制成员的访问方式,可以使用读写方法(getter和setter方法)来实现。
构造方法的返回值类型 : 无
没有返回值类型
在Java中,构造方法是一种特殊的方法,用于创建对象并初始化对象的成员变量。构造方法与普通方法的区别在于,构造方法的名字必须与类名相同,没有返回值类型,并且在创建对象时自动被调用。
因此,构造方法没有返回值类型,也不需要使用return语句来返回值。在构造方法中,我们可以使用this关键字来引用当前对象,从而为对象的成员变量赋初值。例如:
public class Person {
private String name;
private int age;
// 构造方法
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// getter和setter方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
在上面的例子中,Person类有一个构造方法,用于初始化对象的name和age成员变量。
构造方法的名字与类名相同,没有返回值类型。在构造方法中,我们使用this关键字来引用当前对象,并为对象的成员变量赋初值。
成员变量和局部变量的区别
作用范围不同
生命周期不同
初始化方式不同
可见性和访问权限不同
成员变量(Instance Variable)是定义在类中、方法外的变量,它们属于对象的属性,每个对象都拥有一份独立的成员变量副本。成员变量可以使用访问修饰符(public、private、protected或默认)来控制其可见性和访问权限。成员变量在对象创建时被初始化,它们的值可以通过对象访问或者通过对象的方法来修改。
局部变量(Local Variable)是定义在方法或代码块内部的变量,它们只在定义它们的方法或代码块中有效,超出这个范围就不能被访问。局部变量不需要使用访问修饰符来控制其可见性和访问权限。局部变量必须在使用之前被初始化,否则编译器会报错。
成员变量和局部变量的区别主要有以下几点:
作用范围不同:成员变量的作用范围是整个类,而局部变量的作用范围仅限于方法或代码块内部。
生命周期不同:成员变量的生命周期与对象的生命周期相同,对象销毁时成员变量也会被销毁,而局部变量的生命周期仅限于方法或代码块的执行期间,方法或代码块结束时局部变量会被销毁。
初始化方式不同:成员变量在对象创建时会被自动初始化,而局部变量必须在使用之前被手动初始化。
可见性和访问权限不同:成员变量可以使用访问修饰符来控制其可见性和访问权限,而局部变量不需要使用访问修饰符来控制其可见性和访问权限。
需要注意的是,如果在方法或代码块内部定义了与成员变量同名的局部变量,局部变量会覆盖成员变量的值。
如果需要访问成员变量而不是局部变量,可以使用this关键字来引用当前对象的成员变量。
重载和重写
重载(Overloading)和重写(Overriding)是Java中两个重要的概念,它们都是实现多态性的重要手段,但是它们的含义和使用场景不同。
重载(Overloading)是指在同一个类中定义多个方法,它们具有相同的名字但参数列表不同(参数类型、参数个数或参数顺序不同),编译器会根据调用时的实参类型和数量来自动选择相应的方法。重载可以提高代码的复用性和可读性,但是方法的签名必须不同才能进行重载。
例如:
public class Calculator {
public int add(int a, int b) {
return a + b;
}
public double add(double a, double b) {
return a + b;
}
}
在上面的例子中,Calculator类中定义了两个名为add的方法,它们的参数列表不同(一个是int类型,一个是double类型),因此它们可以共用同一个方法名。在调用add方法时,编译器会根据传入的参数类型自动选择调用哪个方法。
重写(Overriding)是指子类覆盖父类的同名方法,实现多态性。重写要求子类方法与父类方法的方法名、返回类型和参数列表完全相同,访问权限不能更低,抛出的异常类型不能更多。
重写可以实现子类对父类方法的修改或扩展,但是不能改变方法的基本特征。
例如:
public class Animal {
public void eat() {
System.out.println("Animal is eating.");
}
}
public class Cat extends Animal {
@Override
public void eat() {
System.out.println("Cat is eating.");
}
}
在上面的例子中,Cat类继承了Animal类,并重写了其eat方法。在调用eat方法时,如果对象是Cat类型,则会调用Cat类中的eat方法;
如果对象是Animal类型,则会调用Animal类中的eat方法。由于Cat类重写了Animal类的eat方法,因此Cat类的对象调用eat方法时会输出“Cat is eating.”。
静态方法能不能使用this和super,为什么?
静态方法是属于类的方法,不属于任何对象,因此不能使用this关键字来引用当前对象。this关键字表示当前对象的引用,而静态方法不需要通过对象来调用,因此不能使用this关键字。
另外,静态方法也不能使用super关键字来调用父类的静态方法。super关键字用于引用父类的成员变量和方法,但是静态方法没有重写(Override)的概念,因此不存在父类和子类的静态方法的关系。
需要注意的是,静态方法只能访问静态成员变量和静态方法,不能访问非静态成员变量和非静态方法。
这是因为静态方法在对象创建之前就已经存在,而非静态成员变量和非静态方法必须在对象创建之后才能访问。
如果需要访问非静态成员变量和非静态方法,可以通过创建对象来实现。
总之,静态方法不能使用this关键字来引用当前对象,也不能使用super关键字来调用父类的静态方法。静态方法只能访问静态成员变量和静态方法,不能访问非静态成员变量和非静态方法。
抽象类和接口
抽象类和接口是Java中两种不同的机制,它们都可以用于实现面向对象编程中的多态性和封装性,但是它们的用途和特点有所不同。
抽象类(Abstract Class)是一种不能被实例化的类,它通常用于作为其他类的基类,其中可能包含一些已经实现的方法和一些待实现的抽象方法。抽象方法没有方法体,由子类来实现。抽象类可以包含实例变量、构造方法和非抽象方法,但是不能被实例化。
抽象类的特点包括:
抽象类不能被实例化,只能被继承;
抽象类可以包含一些已经实现的方法和一些待实现的抽象方法;
抽象方法只有方法签名,没有方法体,由子类来实现;
子类必须实现父类中的所有抽象方法,否则子类也必须声明为抽象类;
抽象类可以包含实例变量、构造方法和非抽象方法;
抽象类不能被final修饰,因为它需要被继承。
接口(Interface)是一种特殊的抽象类,它定义了一组方法的签名,但是没有任何实现。接口中的方法都是抽象方法,没有方法体,由实现接口的类来实现。接口可以包含常量和默认方法(带有默认实现的方法),但是不能包含实例变量和构造方法。
接口的特点包括:
接口定义了一组方法的签名,所有实现该接口的类都必须实现这些方法;
接口中的方法都是抽象方法,没有方法体,由实现接口的类来实现;
接口可以包含常量和默认方法(带有默认实现的方法),但是不能包含实例变量和构造方法;
类可以实现多个接口,从而具有多种行为;
接口可以被扩展,一个接口可以继承另一个接口。
抽象类和接口都是用于实现多态性和封装性的重要机制,它们的使用场景和特点有所不同。通常情况下,如果需要定义一些已经实现的方法和一些待实现的抽象方法,可以使用抽象类;如果需要定义一组方法的签名,但是不关心具体实现,可以使用接口。需要根据具体情况来选择合适的机制来实现代码的设计。
方法的签名
在Java中,方法的签名(Signature)是指方法名和参数类型组成的信息,用于区分不同的方法。
方法的签名包括方法名和参数列表的类型和顺序,但不包括方法的返回类型和访问修饰符。
例如,以下是两个签名不同的方法:
public void print(int num, String text) {
// ...
}
public void print(String text, int num) {
// ...
}
这两个方法的方法名都是print,但是它们的参数列表的类型和顺序不同。因此,这两个方法的签名是不同的,它们可以共用同一个方法名,但是它们是不同的方法。
在Java中,方法的签名在重载(Overloading)中起到重要的作用。如果在同一个类中定义了多个方法名相同但参数列表不同的方法,编译器会根据调用时传入的实参类型和数量来自动选择相应的方法。因此,方法的签名必须不同才能进行重载。如果两个方法的签名相同,编译器会报错。
需要注意的是,方法的返回类型和访问修饰符不属于方法的签名范畴,因此不同的方法可以有相同的返回类型和访问修饰符。
单继承,多继承
单继承(Single Inheritance)和多继承(Multiple Inheritance)是面向对象编程中的两种继承方式,它们的主要区别在于一个类是否可以同时继承多个父类。
单继承(Single Inheritance)是指一个类只能继承一个父类。在Java中,每个类都隐式继承自Object类,因此Java中所有的类都是单继承的。单继承可以简化类之间的关系,减少类之间的耦合度,使继承关系更加清晰。
例如:
public class Animal {
// ...
}
public class Cat extends Animal {
// ...
}
在上面的例子中,Cat类继承自Animal类,它只继承了一个父类。
多继承(Multiple Inheritance)是指一个类可以同时继承多个父类。多继承可以实现代码的复用和灵活性,但是也增加了类之间的耦合度和复杂性,容易导致歧义和冲突。
在Java中,由于多继承可能导致歧义和冲突,因此Java不支持多继承。但是Java可以通过接口来实现类似于多继承的效果。一个类可以实现多个接口,从而具有多种行为。
例如:
public interface Flyable {
void fly();
}
public interface Swimmable {
void swim();
}
public class Duck implements Flyable, Swimmable {
public void fly() {
// ...
}
public void swim() {
// ...
}
}
在上面的例子中,Duck类实现了Flyable和Swimmable接口,从而具有飞行和游泳的行为。尽管Duck类没有直接继承任何父类,但是通过实现接口,它可以具有多种行为,实现了类似于多继承的效果。
总之,单继承和多继承是面向对象编程中的两种继承方式,它们的主要区别在于一个类是否可以同时继承多个父类。在Java中,所有的类都是单继承的,但是可以通过实现接口来实现类似于多继承的效果。
集合框架 list set Map
集合框架(Collections Framework)是Java中提供的一个用于存储和操作数据的框架,它包括了一系列接口和类,用于表示和操作不同类型的数据集合。其中,List、Set和Map是三个常用的接口,分别代表了列表、集合和映射。
List(是Java集合框架中表示列表(List)的接口。)
List是一个有序的集合,它可以存储重复的元素。List中的元素可以根据索引(下标)进行访问和操作。List的实现类包括ArrayList、LinkedList和Vector等。
例如:
List<String> list = new ArrayList<String>();
list.add("apple");
list.add("banana");
list.add("orange");
System.out.println(list.get(1)); // 输出 "banana"
List的常用实现类包括:
ArrayList:基于数组实现的List,支持快速随机访问和增删操作,适用于大量随机访问和尾部增删的场景;
LinkedList:基于链表实现的List,支持快速的头部和尾部增删操作,适用于大量头部和尾部增删的场景;
Vector:与ArrayList类似,但是是线程安全的,支持同步访问。
例如,可以用以下代码创建一个ArrayList,并向其中添加一些元素:
List<String> list = new ArrayList<String>();
list.add("apple");
list.add("banana");
list.add("orange");
这样就创建了一个包含三个元素的列表,可以通过get()方法来访问其中的元素:
System.out.println(list.get(1)); // 输出 "banana"
Set(是Java集合框架中表示集合(Set)的接口。)
Set是一个无序的集合,它不允许存储重复的元素。Set中的元素没有顺序,不能根据索引进行访问和操作。Set的实现类包括HashSet、TreeSet和LinkedHashSet等。
例如:
Set<String> set = new HashSet<String>();
set.add("apple");
set.add("banana");
set.add("orange");
System.out.println(set.contains("banana")); // 输出 "true"
Set的常用实现类包括:
HashSet:基于哈希表实现的Set,不保证元素的顺序,元素存储的顺序取决于哈希值的分布,具有快速查找的优点;
TreeSet:基于红黑树实现的Set,可以按照元素的自然排序或指定的比较器进行排序,具有有序性的优点;
LinkedHashSet:基于哈希表和链表实现的Set,保证元素的插入顺序,具有快速访问和有序性的优点。
例如,可以用以下代码创建一个HashSet,并向其中添加一些元素:
Set<String> set = new HashSet<String>();
set.add("apple");
set.add("banana");
set.add("orange");
这样就创建了一个包含三个元素的集合,可以通过contains()方法来判断一个元素是否在集合中:
System.out.println(set.contains("banana")); // 输出 "true"
Map(是Java集合框架中表示集合(Set)的接口。)
Map是一种键值对(Key-Value)的映射,它允许存储重复的值,但是不允许重复的键。Map中的元素可以根据键进行访问和操作。Map的实现类包括HashMap、TreeMap和LinkedHashMap等。
例如:
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("apple", 1);
map.put("banana", 2);
map.put("orange", 3);
System.out.println(map.get("banana")); // 输出 2
Map的常用实现类包括:
HashMap:基于哈希表实现的Map,不保证元素的顺序,可以快速的通过键查找值;
TreeMap:基于红黑树实现的Map,可以按照键的自然排序或指定的比较器进行排序,具有有序性的优点;
LinkedHashMap:基于哈希表和链表实现的Map,保证元素的插入顺序,具有快速访问和有序性的优点。
例如,可以用以下代码创建一个HashMap,并向其中添加一些键值对:
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("apple", 1);
map.put("banana", 2);
map.put("orange", 3);
这样就创建了一个包含三个键值对的Map,可以通过get()方法来根据键获取对应的值:
System.out.println(map.get("banana")); // 输出 2
需要注意的是,List、Set和Map是三个不同的接口,它们的实现类有所不同,具有不同的特点和用途。在使用集合框架时,需要根据具体的需求来选择合适的接口和实现类来存储和操作数据集合。