创作不易,如果觉得这篇文章对你有帮助,欢迎各位老铁点个赞呗,您的支持是我创作的最大动力!
文章目录
前言
越来越感觉到基础知识的薄弱,所以,这里作一下总结,加深下自己对基础知识的理解。Java基础知识实在是太多了,这里打算持续更新,总结下常见的面试题和知识点。
博主打算每期更新10个面试知识点,后续慢慢迭代更新。
博主这里总结的目的在于,为童鞋们提供面试的参考知识点之外,博主也可以巩固自己的基础知识,工作几年的人都知道,如果稍微不留神,慢慢的,你的基础知识忘记的差不多了
。
面试基础知识总结
1 Java中的运算符有哪些
Java中有大概六种运算符,分别是算术运算符
、连接运算符
、赋值运算符
、关系运算符
、逻辑运算符
和三目运算符
。
1.1 算术运算符
算数运算符大概有7种:+
-
*
/
%
(求余数,取模) ++
--
-
+ 运算符
当+两边的操作数都是数值类型时,则做加法运算 -
- 运算符
当做减法运算时,必须保证两个操作数都是数值类型 -
* 运算符
该运算符用于计算两个数值的乘积 -
/ 运算符
该运算符用于两个数值进行相除,需要注意的是,除数不能为0 -
% 运算符
该运算符用于求余数,即进行取模。就是如果被除数 % 除数可以整除,返回结果为0,否则返回的结果为余数举例:
@Test public void test1() { System.out.println(2 % 3);//2 System.out.println(2 % 2);//0 System.out.println(3 % 2);//1 System.out.println(11 % 4);//3 }
执行结果为:
2 0 1 3
-
++ 运算符
++
运算符表示自身加1
++
运算符又分为:前置++、后置++前置++:将++编写在变量名称前面,先自身加1,然后再做其他运算
int a = 3; int b = ++a; //a = 4 b = 4
后置++:将++编写在变量名称后面,先做其他运算,然后再自身加1
int x = 5; int y = x++; //y = 5 x = 6
代码示例:
@Test public void test2() { //前置++ int a = 5; int b = ++a; System.out.println("前置++结果是:a= " + a + ",b= " + b); //前置++结果是:a= 6,b= 6 通过结果说明,前置++先进行了自身+1操作,后进行了赋值运算 //后置++ int num = 5; int numTwo = num++; System.out.println("后置++结果是:num= " + num + ",numTwo= " + numTwo); //后置++结果是:num= 6,numTwo= 5 结果说明,后置++先进行了赋值运算,后进行了自身+1操作 }
执行结果:
前置++结果是:a= 6,b= 6 后置++结果是:num= 6,numTwo= 5
-
-- 运算符
--
运算符表示自身减1
--
运算符又分为前置、后置,这个的具体使用和++
运算符类似,这里不再阐述
1.2 连接运算符
连接运算符+
作用:求和、连接
当+两边的操作数都是数值类型时,则做加法运算
当+两边的操作数中,只要有一个为非数值类型,则做字符串连接,最终连接后的结果为String类型。
1.3 赋值运算符
-
赋值运算符:
=
表示将右侧的值赋给左侧的变量名称如:int a = 10;
表示把值10赋值给左边的变量a -
扩展赋值运算符大概有5种:
+=
-=
*=
/=
%=
当使用扩展赋值运算符时,变量最终的数据类型没有发生改变
举例:
@Test public void test3() { byte a = 3; //a = a + 10;//这种写法会报编译时错误,因为 = 号后面计算的结果为int类型,但是编译时不进行结果的计算,直接把int赋值给byte会提示编译错误,需要byte,找到int a += 10;//计算过程等价于a = (byte)(a + 10); 扩展赋值运算符a += 10;这种写法就可以,因为扩展赋值运算符时,变量最终的数据类型没有发生改变,但是,可能损失精度 System.out.println(a);//13 int x = 15; //x = x + 0.5; //出现编译错误,需要int,发现double x += 0.5;//计算过程等价于x = (int)(x + 0.5) System.out.println(x);//结果x = 15 }
1.4 关系运算符
关系运算符大概有6种:>
<
>=
<=
==
!=
关系运算符:最终结果为boolean类型的值
运算符计算时优先级别:算术运算符 > 关系运算符 > 赋值运算符
1.5 逻辑运算符
逻辑运算符分类大概有6种:
逻辑与&
逻辑或|
逻辑异或^
逻辑非!
短路与&&
短路或||
-
逻辑与
&
,表示并且
当两个条件同时为true时,则结果为true,否则结果为false -
逻辑或
|
,表示或者
当两个条件中有一个为true时,则结果为true,否则为false -
逻辑异或
^
当两个条件的值不同时,则结果为true,否则为false
true ^ true ------- > false
true ^ false------- > true
fasle ^ false ------ > false
false ^ true ------ > true -
逻辑非
!
表示对boolean类型的值进行取反 -
短路与
&&
,类似于逻辑与,都表示并且
短路与、逻辑与运行结果都相同,但是执行过程可能不同,当使用短路与,并且第一个条件为false时,则结果直接为false,不会进行第二个条件的判断,而短路与会进行后面条件的判断。 -
短路或
||
,类似于逻辑或,都表示或者
短路或、逻辑或运行结果都相同,但是执行过程可能不同,当使用短路或,并且第一个条件为true时,则结果直接为true
总结:
逻辑运算符,最终结果为boolean类型
博主建议使用短路与和短路或,这样有些场景下可以减少一次判断,有利于提高性能
1.6 三目运算符
三目运算符也叫做条件运算符,Java中常用于不同条件下赋不同的值
语法格式: 条件 ? 代码1 : 代码2
举例:
@Test
public void test4() {
int x = 15;
System.out.println(x == 15 ? "yes" : "no");//yes
}
2 =与==、&与&&、|与||有什么区别
-
= 与 ==
=
属于赋值运算符,将右侧的值赋给左侧的变量名称
==
属于关系运算符,如果两边是数值类型,判断左右两边的值是否相等。如果左右两边是引用类型,比较的是内存地址是否相等,计算的结果为boolean类型 -
& 与 &&
&
是逻辑与,&&
是短路与,都属于逻辑运算符,都表示并且,执行结果都相同
不同的是,当使用短路与&&
时,如果第一个条件为false时,不会进行后面的判断,结果直接为false -
| 与 ||
|
是逻辑或,||
是短路或,都属于逻辑运算符,都表示或者,结果都相同
当使用短路或||
时,并且第一个条件为true时,则结果直接为true
3 说说Java中的switch case穿透现象
switch case中的case穿透现象指的是: 由于break是可有可无的,当没有编写break,则从当前第一个匹配的case一直向下执行,下一个case的java语句也会执行,这种现象就是case穿透
。
代码举例:
/**
* Java中case穿透现象演示
*/
@Test
public void test6() {
char a = 'A';
switch (a) {
case 'A':
System.out.println("执行结果:A");
case 'B':
System.out.println("执行结果:B");
case 'C':
System.out.println("执行结果:C");
break;
case 'D':
System.out.println("执行结果:D");
break;
default:
break;
}
}
执行结果:
执行结果:A
执行结果:B
执行结果:C
以上执行结果也说明了这一点,虽然只有A匹配上了,但是由于A和B没有写break,打印了"执行结果:A"之后,又向下一个case执行了,打印了“执行结果:B”和“执行结果:C”,直到遇到了C中的break,程序才结束。
switch case语法基础介绍:
-
switch case:只能做等值操作
语法格式:
switch(表达式){ case 常量值1: break; case 常量值2: break; default: break; }
-
switch case中,表达式的值的类型
基本数据类型:java 1.6(包含)以前可以是byte
、short
、int
、char
,因为(byte、short、char)类型的值可以默认转换为int类型JDK5以后可以是
枚举类型
在JDK1.7及以后还可以是
String类型
。不支持long、float、double、boolean四种基本类型
在测试时发现,包装器类型Byte,Short,Character,Integer后发现都可以正确打印,于是便说switch也支持byte,short,char,int的包装类。这种说法不完全正确,之所以switch能够支持他们的包装类,是因为自动拆箱(从JDK1.5开始支持自动拆箱和自动装箱,自动拆箱就是自动将引用数据类型转化为基本数据类型,自动装箱就是自动将基本数据类型转化为引用数据类型)的原因。
-
在switch中可以编写任意多个case
-
case后面常量值的类型必须与表达式的类型一致
-
break 表示中断,当遇到break则执行switch case外面的语句
-
default是可有可无的,如果有default,一个switch中最多编写一个default,当所有case都不满足时则执行default
-
常见的注意事项:
a、case后面常量值的顺序可以任意,一般按顺序编写
b、default顺序可以编写在switch中的任意位置
c、当所有case都不满足时则执行default
d、建议default编写在所有case的后面
e、case也可以合并
如:switch(表达式){ case 常量值1: case 常量值2: java语句; break; default: break; } @Test public void test5() { char a = 'A'; switch (a) { case 'A': case 'B': System.out.println("执行结果:A-B");//执行结果:A-B break; case 'C': System.out.println("执行结果:C"); break; default: break; } }
f、break是可有可无的,当没有编写break,则从当前第一个匹配的case一直向下执行(也就是
穿透
)因此,开发中,需要根据需求编写合适的break
4 Java中break、continue、return有什么区别
-
使用场合不同
break: 表示中断,可以在switch case中使用,也可以在循环中使用continue: 表示继续,只能在循环中使用
return: 表示返回,只能在方法中使用
-
作用不同
break: 表示中断,当在switch case中或在循环中遇到break,则结束当前整个switch case或循环,执行外面的语句continue: 表示继续,当在循环中遇到continue,则结束当次(本次)循环,继续执行下一次循环
return: 表示返回结果,当方法中遇到return时,则返回到方法的调用处
注意:return有一种特殊形式,当方法是无返回类型时,则可以在方法体中编写return,但是必须编写为return;
5 Java中方法的重载与方法的覆盖
请参考我的博客:Java中方法的重载与方法的覆盖
6 Java中抽象类与接口有什么区别
请参考我的博客:Java中抽象类与接口
7 说一说Java中,什么是类,什么是对象
-
类
Java中的类
是一种概念,是一种数据引用类型。类就是现实世界中,具有共同特征的一堆事物,对这些事物进行抽象,就形成了类
。实际上,类并不存在,类只是人类大脑抽象的结果。类
是对具有共性事物的抽象描述,是在概念上的一个定义,类不是具体的。那么,如何发现类呢?通常根据名词(概念)来发现类。比如说:成绩管理系统中,学生、老师、班级、课程等,都可以抽象为一个类。类的定义:
格式:类的修饰符 class 类名 extends 父对象的名称 implements 接口名称
如:public class Student extends Person implements SpeakInterface
描述对象的特征,称为
属性
,如:颜色、价格、尺寸…….
对象所做的事情,称为方法
或行为
如:张三对象 属性:名字、性别、年龄、身高、体重、住址 方法:学习、说话、吃饭 李四对象 属性:姓名、年龄、住址、性别 方法:睡觉、学习、游泳
将多个对象找到相同的属性和方法组合在一起,就形成了类
。简单来说,可理解为
类 = 属性 + 方法
,属性(成员变量)来源于类的状态,而方法来源于动作(成员方法)。 -
对象
万物皆对象,也就是说,对象是一个具体的实例。对象是一种实际存在的个体,从类到对象的过程,就是具体化(实例化)的过程
。
从对象到类的过程,就是抽象的过程
。总的来说,对共性事物进行了抽象,就形成了类,这些类具体化,就成为实例(对象)。也就是说,一个类的具体化(实例化)的过程,就是对象(实例)。
8 谈一谈面向对象与面向过程
请参考我的博客:Java基础知识总结 中1.8节 面向对象与面向过程的介绍
9 参数传递
面试笔试题中,容易考察参数的传递。
-
基本数据类型作为参数传递
传递的是真正的值,在一个方法中改变变量的值,对另一个方法中变量的值没有任何影响,各自变量是独立的 -
引用数据类型作为参数传递
传递的是地址,也就数说多个引用名称共用同一个对象
具体的,可以参考我的另一篇博文Java中参数传递,进行深入了解。
10 请谈谈你对Java多态的理解
10.1 多态是什么
多态(Polymorphism)按字面的意思就是“多种状态”。在面向对象语言中,接口的多种不同的实现方式即为多态。用白话来说,就是多个对象调用同一个方法,得到不同的结果。
10.2 多态的语法格式
父类类名 引用名称 = new 子类类名();
当是多态时,该引用名称只能访问父类中的属性和方法,但是访问的时候,会优先访问子类重写以后的方法。
10.3 满足多态的条件
- 子类必须继承父类
- 子类必须重写父类的方法
- 父类引用指向子类对象,即:父类类名 引用名称 = new 子类类名();
10.4 使用多态好处
-
使用多态可以使代码之间的
耦合度
降低 -
减少冗余代码的同时,也使得项目的扩展能力更强
注:耦合度指的是代码(程序)之间的关联程度
10.5 多态中的类型转换
Java多态中,有两种类型转换:向上转型
和向下转型
-
向上转型
向上转型,也叫做自动类型转换,子类型赋值给父类型(父类型的引用指向子类型),构成多态
父类类型 引用名称 = new 子类类名();当使用多态方式调用方法时,该引用名称只能访问父类中的属性和方法。编译器首先检查父类中是否有该方法,如果没有,则编译错误。如果有,再去调用子类的同名(重写)方法。
-
向下转型
向下转型,也叫做强制类型转换,父类型赋值给子类型当使用多态时,并且访问子类独有的属性或方法时,则必须进行向下转型
当进行向下转型时,建议先使用
instance of
关键字进行判断,判断合法时,则在转为对应的类型,否则可能会出现类型转换异常 java.lang.ClassCastException。说明:
instance of
关键字用于判断一个对象,是否属于某个指定的类或其子类的实例。
10.6 多态的实现方式
-
普通子类重写父类方法
-
接口
生活中的接口最具代表性的就是插座,例如一个三接头的插头都能接在三孔插座中,因为这个是每个国家都有各自规定的接口规则,有可能到国外就不行,那是因为国外自己定义的接口类型。USB接口也很典型,有了这个,使得接口统一,生活更加方便
-
抽象类和抽象方法
10.7 多态简单使用案例
场景:假如有个饲养员,需要给不同的宠物喂食,下面给出使用多态和不使用多态的实现方式。
不使用多态的实现:
首先定义一个抽象类Animal
、一个饲养员类AnimalKeeper
、一个宠物类Dog
和一个宠物类Cat
。
public abstract class Animal {
public void eat() {
System.out.println("动物吃东西!");
}
}
/**
* 饲养员
*/
public class AnimalKeeper {
/**
* 给宠物猫喂食
*
* @param cat
*/
public void feed(Cat cat) {
cat.eat();
}
/**
* 给宠物狗喂食
*
* @param dog
*/
public void feed(Dog dog) {
dog.eat();
}
}
public class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗啃骨头!");
}
}
public class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼!");
}
}
测试类:
public class PolymorphicTest {
public static void main(String[] args) {
//创建饲养员对象
AnimalKeeper animalKeeper = new AnimalKeeper();
//创建宠物对象
Cat cat = new Cat();
animalKeeper.feed(cat);//猫吃鱼!
Dog dog = new Dog();
animalKeeper.feed(dog);//狗啃骨头!
}
}
以上实现看起来没有什么问题,也容易理解,在目前情况下,饲养员可以满足喂养宠物的需求。但是,过了一周,饲养员又喂养了一只鸟,这时候不得不修改AnimalKeeper类,使其可以饲养宠物鸟,不仅违反了Java中的开闭原则,而且以上代码的实现,扩展性极差。
使用多态的实现:
只需要对以上代码中,饲养员类AnimalKeeper
进行替换,新增一个饲养员类AnimalKeeperPolymorphic
类。
/**
* 饲养员
*/
public class AnimalKeeperPolymorphic {
/**
* 饲养员给宠物喂食
*
* @param animal
*/
public void feed(Animal animal) {
animal.eat();
}
}
测试用例:
public static void change() {
//创建饲养员对象
AnimalKeeperPolymorphic animalKeeper = new AnimalKeeperPolymorphic();
//创建宠物对象
Cat cat = new Cat();
animalKeeper.feed(cat);//猫吃鱼!
Dog dog = new Dog();
animalKeeper.feed(dog);//狗啃骨头!
}
这种实现有什么好处呢,当新需求来了,需要扩展时,不需要修改饲养员的代码。比如说刚才那个需求,新增加一个宠物鸟,只需要新建一个宠物鸟类,实现Animal接口,不仅遵循了OCP
原则,也可以实现饲养宠物鸟的功能。
10.8 多态分析
以上文中示例代码进行分析,看看多态是如何使用的。
AnimalKeeperPolymorphic中的feed方法,使用了多态。
当饲养员喂养宠物狗时,其实执行的是:
Animal animal = new Dog();
当饲养员喂养宠物猫时,其实执行的是:
Animal animal = new Cat();
这种属于向上转型,里面有继承(cat继承Animal)关系,重写了父类eat方法,子类型赋值给父类型(父类型的引用指向子类型),构成了多态。
Animal animal
= new Cat(); 程序在编译阶段,animal引用类型
被编译器看做Animal类型,所以程序在编译阶段,animal引用
绑定的是Aninmal类中的eat()方法,这个过程叫做Java多态的静态绑定
。
程序在运行的时候,堆中的对象实际上是Cat类型,而Cat对象已经覆盖(重写)了父类Animal的eat()方法,所以程序在运行阶段,对象绑定的方法是Cat中的eat()方法,这个过程叫做Java多态的动态绑定
。
写博客是为了记住自己容易忘记的东西,另外也是对自己工作的总结,希望尽自己的努力,做到更好,大家一起努力进步!
如果有什么问题,欢迎大家一起探讨,代码如有问题,欢迎各位大神指正!
给自己的梦想添加一双翅膀,让它可以在天空中自由自在的飞翔!