文章目录
面试问答标准答案
java原理类
(1)解释java的跨平台性原理
答:java程序并不能直接运行,java编译器把程序编译成与平台无关的字节码文件(.class文件),然后通过下载不同操作系统的jvm虚拟机,将字节码文件编译为.java程序。
(2)JVM,JRE,JDK是什么?以及它们的包含关系。
答:JVM是java虚拟机
JRE是java运行环境,包含JVM和java核心类库
JDK是java开发工具,包含了jre和开发工具。
(3)JDK的安装目录中各部分成员的说明。
(4)常用DOS命令。
(5)为什么要配置环境变量:
开发中我们设计的java程序需要用到的命令放在jvm的bin文件夹下面,如果不配置则命令只能在该文件目录下执行。所以配置环境变量的作用就是让bin文件夹下的java相关命令可以在任意文件夹下面执行。
其中JAVA_HOME是告诉操作系统jdk的安装位置,Path是告诉操作系统jdk的bin文件夹下面的命令安装到哪个位置。
基本语法类
(1)多行注释和文档注释的区别
/*
可能这就是爱
*/
/**
这是文档注释
*/
(2)计算机底层用(数字电路)来实现,开表示0,关表示1.数据在底层采用(二进制)存储,我们认为计算机的一个开关称为一位。(字节)是计算机数据的最小单位,计算机存储的最小信息单元称为(位),计算机最基本的存储单元叫(字节)
(3)java的基本数据类型包含什么:
1. 定义long类型的变量时,需要在整数的后面加L(大小写均可,建议大写)。因为整数默认是int类型,整数太大可能超出int范围。
2. 定义float类型的变量时,需要在小数的后面加F(大小写均可,建议大写)。因为浮点数的默认类型是double, double的取值范围是大于float的,类型不兼容。
(4)解释类型转换
1.自动类型转换:类型范围小的变量可以直接赋值给类型范围大的变量。表达式中小类型的变量会自动转换为较大范围的类型再去进行运算.在表达式中,byte、short、char 是直接转换成int类型参与运算的。
2.强制类型转换:类型范围大的变量,不能直接赋值给类型小的变量,会报错,必须强制转换。
double num1=5.5;
int num2=(int) num1;
Note:
char类型的数据转换为int类型是按照码表中对于的int值进行计算的。
int a = 'a';
System.out.println(a); // 将输出97
整数默认是int,byte,short和char类型的数据参与运算都会自动转换为int类型。
byte b1 = 10;
byte b2 = 20;
byte b3 = b1 + b2;
// 第三行代码会报错,b1和b2会自动转换为int类型,计算结果为int,int赋值给byte需要强制类型转换。
// 修改为:
int num = b1 + b2;
// 或者:
byte b3 = (byte) (b1 + b2);
boolean类型不能和其他数据类型相互转换。
(5)字符和字符串的“+”操作:
char类型参与算术运算使用的是底层对应的十进制数值(’a’–97 ‘A’–65 ‘0’–48)
在程序开发中我们很少使用byte或者short类型定义整数。也很少会使用char类型定义字符,而使用字符串类型,更不会使用char类型做算术运算。
// 可以通过使用字符与整数做算术运算,得出字符对应的数值是多少
char ch1 = 'a';
System.out.println(ch1 + 1); // 输出98,97 + 1 = 98
char ch2 = 'A';
System.out.println(ch2 + 1); // 输出66,65 + 1 = 66
char ch3 = '0';
System.out.println(ch3 + 1); // 输出49,48 + 1 = 49
字符串中的“+”是字符串连接符
System.out.println(1 + 99 + "年黑马"); // 输出:199年黑马
System.out.println(1 + 2 + "itheima" + 3 + 4); // 输出:3itheima34
// 可以使用小括号改变运算的优先级
System.out.println(1 + 2 + "itheima" + (3 + 4)); // 输出:3itheima7
(6)方法易考点:
- 每个方法在被调用执行的时候,都会进入栈内存,并且拥有自己独立的内存空间,方法内部代码调用完毕之后,会从栈内存中弹栈消失。
- 基本数据类型的参数,形式参数的改变,不影响实际参数。引用数据类型的传参,传入的是地址值,内存中会造成两个引用指向同一个内存的效果,所以即使方法弹栈,堆内存中的数据也已经是改变后的结果
- 方法不能嵌套定义。
面向对象
(1)多个对象在堆内存中,都有不同的内存划分,成员变量存储在各自的内存区域中,成员方法多个对象共用的一份。只要有任何一个对象修改了内存中的数据,随后,无论使用哪一个对象进行数据获取,都是修改后的数据。
(2)成员变量和局部变量的区别:
- 类中位置不同:成员变量(类中方法外)局部变量(方法内部或方法声明上)
- 内存中位置不同:成员变量(堆内存)局部变量(栈内存)
- 生命周期不同:成员变量(随着对象的存在而存在,随着对象的消失而消失)局部变量(随着方法的调用而存在,随着方法的调用完毕而消失)
- 初始化值不同:成员变量(有默认初始化值)局部变量(没有默认初始化值,必须先定义,赋值才能使用)
(3)封装的好处与原则
将类的某些信息隐藏在类的内部(private只允许类内访问),不允许外部程序直接访问,而是通过该类提供的方法实现对隐藏信息的操作和访问。
好处:通过方法控制成员变量的操作,提高代码的安全性。把代码用方法封装,提高代码复用性。
(4)this主要是用于指代成员变量,区分成员变量和局部变量的重名问题。方法的形参如果与成员变量同名,不带this修饰的变量指的是形参,而不是成员变量.
(5)static:
1.static修饰的成员变量表示该变量只在内存中存储一份,可以被共享访问修改。静态成员变量属于类,实例变量属于对象。
类名.成员变量(推荐)
2.工具类定义的都是一些静态方法,每个方法以完成一个功能为目的。以来可以调用方便,二来可以提高代码的复用性。建议把工具类的构造器私有,工具类无法创建对象。里面都是静态方法,直接用类名访问即可。
3.静态方法中是不可以出现this关键字的。
(6)继承
1.子类可以继承父类的属性和行为,但是子类不能继承父类的构造器。子类可以继承父亲的私有成员变量,但是不能直接访问。
子类中所有的构造器默认都会先访问父类中无参的构造器,再执行自己.默认是super()
如果父类中没有无参数构造器,只有有参构造器,会报错。因为子类默认是调用父类无参构造器的。
子类构造器中可以通过书写 super(…),手动调用父类的有参数构造器
super调用父类构造器的作用:通过调用父类有参数构造器来初始化继承自父类的数据
2.在子类方法中访问成员(成员变量、成员方法)满足:就近原则。先子类局部范围找,然后子类成员范围找,然后父类成员范围找,如果父类范围还没有找到则报错。
3.如果子父类中出现了重名的成员变量,优先访问子类的,如果想访问父类用super调用。
(7)方法重写:在继承体系中,子类出现了和父类中一模一样的方法声明,我们就称子类这个方法是重写的方法
- 情况:当子类需要父类的功能,但父类的该功能不完全满足自己的需求时。子类可以重写父类中的方法。
- @Override重写注解
@Override是放在重写后的方法上,作为重写是否正确的校验注解。加上该注解后如果重写错误,编译阶段会出现错误提示。建议重写方法都加@Override注解,代码安全。 - 重写方法的名称、形参列表必须与被重写方法的名称和参数列表一致。私有方法不能被重写。子类重写父类方法时,访问权限必须大于或者等于父类 (暂时了解 :缺省 < protected < public).子类不能重写父类的静态方法,如果重写会报错的。
(8)多态
1.同类型的对象,执行同一个行为,会表现出不同的行为。
父类类型 对象=new 子类构造器;
接口 对象=new 实现类构造器;
2.多态中成员访问特点
方法调用:编译看左边,运行看右边。
变量调用:编译看左边,运行也看左边。
3.多态的前提
有继承/实现关系;有父类引用指向子类对象;有方法重写。
4.多态优点:使用父类型作为参数,可以接受这父类的一切子类对象+右边对象可以实现解耦合便于维护。
5.自动类型转换(从子到父):子类对象赋值给父类类型的变量指向。
强制类型转换(从父到子)
子类 对象变量 = (子类)父类类型的变量
作用:可以解决多态下的劣势,可以实现调用子类独有的功能。
注意: 如果转型后的类型和对象真实类型不是同一种类型,那么在转换的时候就会出现ClassCastException
(9)内部类
名称 | 属性 | 特点 |
---|---|---|
静态内部类 | 外部类名.内部类名 对象名=new 外部类名.内部构造器(); | 可以访问外部类静态成员,不能访问外部类实例成员 |
成员内部类 | 外部类名.内部类名 对象名 = new 外部类构造器.new 内部类构造器(); | 可以访问外部类静态成员,可以访问外部类实例成员 |
局部内部类 | 放在方法,代码块,构造器等执行体中 | |
匿名内部类 | 方便创建子类对象 | 是没有名字的内部类。会产生一个匿名内部类的对象。对象类型是当前new的那个的类型的子类类型。 |
(10)包
1.包是用来分门别类管理不同类的(类似于文件夹)
2.建包的语法:package 公司域名倒写.技术名称
3.导包:使用不同包下的类:import 包名.类名
4.如果一个类中需要用到俩个相同名字的不同类,默认只导入一个类,另外一个要带包名访问。
(11)枚举
1.枚举是java中的一种特殊类型,枚举的作用是“为做信息的标志和信息的分类”。
修饰符 enum 枚举名称{
第一行都是罗列枚举类实例的名称
}
2.特征:
(1)枚举继承了枚举类型:java.long.Enum
(2) 枚举都是最终类,不能被继承
(3) 枚举类不能对外创建对象
3.优点:枚举用作信息标志和分类的好处(入参约束严谨,代码优雅,是最好的信息分类技术)
API
String类
(1)特点:字符串不可变,它们的值在创建后不能被更改。虽然 String 的值是不可变的,但是它们可以被共享。字符串效果上相当于字符数组( char[] ),但是底层原理是字节数组( byte[] )
(2)String字符串的创建:
String s=new String();
String s=new String(chs);//字符数组
String s=new String(bys);//字节数组
//通过 new 创建的字符串对象,每一次 new 都会申请一个内存空间,虽然内容相同,但是地址值不同
String s="abc";
//直接赋值的字符串,只要字符序列相同(顺序和大小写),无论在程序代码中出现几次,JVM 都只会建立一个 String 对象,并在字符串池中维护
(3)==:比较基本数据类型比较的值,比较引用数据类型比较的是对象的地址值。
equals比较的是值是否相同,去扽大小写。
(4)String类常用方法:
public boolean equals(Object anObject) | 比较字符串的内容,严格区分大小写(用户名和密码) |
---|---|
public char charAt(int index) | 返回指定索引处的 char 值 |
public int length() | 返回此字符串的长度 |
ArrayList类
public boolean remove(Object o):删除指定的元素,返回删除是否成功
System.out.println(array.remove("world"));
public E remove(int index):删除指定索引处的元素,返回被删除的元素System.out.println(array.remove(1));
public E set(int index,E element):修改指定索引处的元素,返回被修改的元素 System.out.println(array.set(1,"javaee"));
public E get(int index):返回指定索引处的元素
public int size():返回集合中的元素的个数
System.out.println(array.size());
Object类
toString() | 返回当前对象在堆内存中的地址信息:类的权限名@内存地址 | 父类的toString()是为了被子类重写,以便于返回对象的内容信息 |
---|---|---|
equals | 默认是返回一个对象和另一个对象地址是否相等 | 让子类重写来定制比较规则 |
StringBuilder
(1)定义:可变的字符串类,我们可以把它看成一个对象容器。
(2)作用:提高字符串的拼接效率,例如拼接,修改等。
StringBuilder s=new StringBuilder(String str);
append(任意类型):添加数据并且返回本身
reverse():反转对象的内容
length():返回对象长度
toString():将StringBuilder转换为String
Math
System
计算机认为时间是有起点的,起始时间: 1970年1月1日 00:00:00
时间毫秒值:指的是从1970年1月1日 00:00:00走到此刻的总的毫秒数,应该是很大的。 1s = 1000ms。
BigDecimal作用
用于解决浮点型运算精度失真的问题.
Date类
SimpleDataFormat类
完成日期时间的格式化操作。
Calendar
Array
操作数组的API.
易错知识点
(1)扩展赋值运算符隐含强制转换
short s = 10;
s = s + 10; // 此行代码报出,因为运算中s提升为int类型,运算结果int赋值给short可能损失精度
s += 10; // 此行代码没有问题,隐含了强制类型转换,相当于 s = (short) (s + 10);
基础编程题目
1.使用三目运算符比较三个数字大小
public class OperatorTest02 {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int[] arr=new int[3];
for(int i=0;i<=3;i++)
arr[i]=sc.nextInt();
int tempHeight=a[0]>a[i]?a[0]:a[1];
int maxHeight=a[2]>tempheight?a[2]:tempheight;
System.out.println(maxHeight);
}
}
2.求n位的水仙花数
public class ForTest04 {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int n=sc.nextInt();
for(int i=Math.pow(10,n-1);i<Math.pow(10,n);i++)
{
int sum=0;
while(i!=0)
{
sum+=Math.pow(i%10,3);
i/=10;
}
if(sum==i)
System.out.println(sum);
}
}
3.调用random猜测数字
public class RandomTest {
public static void main(String[] args) {
//要完成猜数字的游戏,首先需要有一个要猜的数字,使用随机数生成该数字,范围1到100
Random r = new Random();
int number=r.nextInt(100)+1;
while(true) {
//使用程序实现猜数字,每次均要输入猜测的数字值,需要使用键盘录入实现
Scanner sc = new Scanner(System.in);
System.out.println("请输入你要猜的数字:");
int guessNumber = sc.nextInt();
//比较输入的数字和系统产生的数据,需要使用分支语句。
if(guessNumber > number) {
System.out.println("你猜的数字" + guessNumber + "大了");
} else if(guessNumber < number) {
System.out.println("你猜的数字" + guessNumber + "小了");
} else {
System.out.println("恭喜你猜中了");
break;
}
}
}
4.利用方法重载的思想,设计比较俩个整数是否相同。(方法在同一个类中,名字相同,但是参数类型,个数,参数本身不同)
public class MethodTest {
public static void main(String[] args) {
//调用方法
System.out.println(compare(10, 20));
System.out.println(compare((byte) 10, (byte) 20));
System.out.println(compare((short) 10, (short) 20));
System.out.println(compare(10L, 20L));
}
//int
public static boolean compare(int a, int b) {
System.out.println("int");
return a == b;
}
//byte
public static boolean compare(byte a, byte b) {
System.out.println("byte");
return a == b;
}
//short
public static boolean compare(short a, short b) {
System.out.println("short");
return a == b;
}
//long
public static boolean compare(long a, long b) {
System.out.println("long");
return a == b;
}
}
5.设计一个方法用于数组遍历,要求遍历的结果是在一行上的
System.out.println(“内容”); 输出内容并换行
System.out.print(“内容”); 输出内容不换行
System.out.println(); 起到换行的作用
public class MethodTest01{
public static void main(String[] args) {
//定义一个数组,用静态初始化完成数组元素初始化
int[] arr = {11, 22, 33, 44, 55};
//调用方法
printArray(arr);
}
public static void main(String[] arr){
System.out.print("[");
for(int i=0;i<arr.length;i++)
{
if(i==arr.length-1)
System.out.println(arr[i]);
else
System.out.println(arr[i]+",");
}
System.out.println("]");
}
}
6.我国古代数学家张丘建在《算经》一书中提出的数学问题:鸡翁一值钱五,鸡母一值钱三,鸡雏三值钱一。百钱买百鸡,问鸡翁、鸡母、鸡雏各几何?
public class Test05 {
public static void main(String[] args) {
//第1层循环,用于表示鸡翁的范围,初始化表达式的变量定义为 x=0,判断条件是x<=20
for(int x=0; x<=20; x++) {
//第2层循环,用于表示鸡母的范围,初始化表达式的变量定义为 y=0,判断条件是y<=33
for(int y=0; y<=33; y++) {
//这个时候,用于表示鸡雏的变量 z = 100 – x – y
int z = 100 - x - y;
//判断表达式 z%3==0 和表达式 5*x + 3*y + z/3 = 100 是否同时成立
if(z%3==0 && 5*x+3*y+z/3==100) {
System.out.println(x+","+y+","+z);
}
}
}
}
}
7.有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子, 假如兔子都不死,问第二十个月的兔子对数为多少?
public class Test04 {
public static void main(String[] args) {
//为了存储多个月的兔子对数,定义一个数组,用动态初始化完成数组元素的初始化,长度为20
int[] arr = new int[20];
//因为第1个月,第2个月兔子的对数是已知的,都是1,所以数组的第1个元素,第2个元素值也都是1
arr[0] = 1;
arr[1] = 1;
//用循环实现计算每个月的兔子对数
for(int x=2; x<arr.length; x++) {
arr[x] = arr[x-2] + arr[x-1];
}
//输出数组中最后一个元素的值,就是第20个月的兔子对数
System.out.println("第二十个月兔子的对数是:" + arr[19]);
}
}
8.已知用户名和密码,模拟用户登录,总共给三次机会。
public class StringTest01 {
public static void main(String[] args) {
//已知用户名和密码,定义两个字符串表示即可
String username = "itheima";
String password = "czbk";
for(int i=0;i<3;i++)
{
Scanner sc=new Scanner(System.in);
System.out.println("请输入用户名:");
String name = sc.nextLine();
System.out.println("请输入密码:");
String pwd = sc.nextLine();
if(name.equals(username)&&pwd.equals(passwprd)){
System.out.println("登录成功");
break;}
} else {
if(2-i == 0) {
=System.out.println("你的账户被锁定,请与管理员联系");
} else {
System.out.println("登录失败,你还有" + (2 - i) + "次机会");
}
}
}
}
}
9.反向输出字符串
public static String reverse(String s)
{
String ss="";
for(i=s.length()-1;i>=0;i--)
{
ss+=s.charSt(i);
}
return ss;
}
10.统计字符出现的次数
public class StringTest03 {
public static void main(String[] args) {
//键盘录入一个字符串,用 Scanner 实现
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个字符串:");
String line = sc.nextLine();
//要统计三种类型的字符个数,需定义三个统计变量,初始值都为0
int bigCount = 0;
int smallCount = 0;
int numberCount = 0;
//遍历字符串,得到每一个字符
for(int i=0; i<line.length(); i++) {
char ch = line.charAt(i);
//判断该字符属于哪种类型,然后对应类型的统计变量+1
if(ch>='A' && ch<='Z') {
bigCount++;
} else if(ch>='a' && ch<='z') {
smallCount++;
} else if(ch>='0' && ch<='9') {
numberCount++;
}
}
//输出三种类型的字符个数
System.out.println("大写字母:" + bigCount + "个");
System.out.println("小写字母:" + smallCount + "个");
System.out.println("数字:" + numberCount + "个");
}
}
11.ArrayList存储学生对象并且遍历
public class ArrayListTest {
public static void main(String[] args) {
//创建集合对象
Array<Student> array=new Arraylist<>();
//为了提高代码的复用性,我们用方法来改进程序
addStudent(array);
addStudent(array);
addStudent(array);
//遍历集合,采用通用遍历格式实现
for (int i = 0; i < array.size(); i++) {
Student s = array.get(i);
System.out.println(s.getName() + "," + s.getAge());
}
}
public static void addStudent(ArrayList<Student> array) {
//键盘录入学生对象所需要的数据
Scanner sc = new Scanner(System.in);
String name = sc.nextLine();
String age = sc.nextLine();
//创建学生对象,把键盘录入的数据赋值给学生对象的成员变量
Student s = new Student();
s.setName(name);
s.setAge(age);
//往集合中添加学生对象
array.add(s);
}
}
红黑树
红黑树是一种自平衡的二叉查找树(平衡二叉B树)
(1)每一个节点可以是红或者黑,红黑树不是通过高度平衡的,而是通过红黑规则来实现。
(2)红黑规则:
1)根节点必定是黑色
2)如果某一个节点是红色,子节点必然是黑色
3)对于每一个节点来说,从该节点到其后所有后代叶节点的简单路径上,均包含相同数目的黑色节点。
添加原则
(1)每一个节点或是红色的,或者是黑色的,根节点必须是黑色
(2)如果一个节点没有子节点或者父节点,则该节点相应的指针属性值为Nil,这些Nil视为叶节点,每个叶节点(Nil)是黑色的;
(3)如果某一个节点是红色,那么它的子节点必须是黑色(不能出现两个红色节点相连的情况)
(4)对每一个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点。
hashset底层实现:
Treeset
默认排序规则:
(1)对于数值类型:Integer , Double,官方默认按照大小进行升序排序。
(2)对于字符串类型:默认按照首字符的编号升序排序。
(3)对于自定义类型如Student对象,TreeSet无法直接排序。
法(一):让自定义的类(如学生类)实现Comparable接口重写里面的compareTo方法来定制比较规则。
法(二):TreeSet集合有参数构造器,可以设置Comparator接口对应的比较器对象,来定制比较规则。