(一)【基础知识梳理——JAVAse部分】Java中的变量和常量
在程序中存在大量的数据来代表程序的状态,其中有些数据在程序的运行过程中值会发生改变,有些数据在程序运行过程中值不能发生改变,这些数据在程序中分别被叫做变量和常量,今天我们就面试可能碰到的相关变量和常量的知识进行梳理。
一、什么是变量?
变量代表程序的状态。程序通过改变变量的值来改变整个程序的状态,也就是实现程序的功能逻辑。为了方便的引用变量的值,在程序中需要为变量设定一个名称,这就是变量名。由于Java语言是一种强类型的语言,所以变量在使用以前必须首先声明,如:
int x;
二、什么是常量?
所谓常量,我们可以理解为是一种特殊的变量,它的值被设定后,在程序运行过程中不允许改变,程序中使用常量可以提高代码的可维护性,定义方法为:
final double a=11.3;
三、JAVA中的数据类型有哪些?
整型: byte(8位) short(16位) int(32位)---默认类型 long(64位)
浮点型: float(32位) double(64位)---默认类型
字符型: char(2个字节---16位)
布尔型: boolean
其中这里有些值得注意的点:
① byte:由1个字节8位表示,是最小的整数类型。主要用于节省内存空间关键。当操作来自网络、文件或者其他IO的数据流时,byte类型特别有用。取值范围为:[-128,127]. byte的默认值为(byte)0。
② short:用16位表示,取值范围为:[-2^15, 2^15 - 1]。
③ int :32 bits, [- 2^31, 2^31 - 1].有符号的二进制补码表示的整数。常用于控制循环,注意byte和 short在运算中会被提升为int类型或更高。Java 8以后,可以使用int类型表示无符号32位整数[ 0,2^31 - 1]。
④ long:64 bits, [- 2^63, 2^63- 1,默认值为0L.当需要计算非常大的数时,如果int不足以容纳大小,可以使用long类型。如果long也不够,可以使用BigInteger类。(注意:如,longa = 100000L定义long类型一定要在常量后加上L或l,这是因为常量的默认类型是int,当常量的值超过int值域后就会编译错误)。
⑤ float:使用32 bit表示,对应单精度浮点数,运行速度相比double更快,占内存更小,但是当数值非常大或者非常小的时候会变得不精确。精度要求不高的时候可以使用float类型。(注意:如,float a=1.0f定义float类型一定要在常量后加上F或f,因为默认的浮点型是double,而double不能自动转换为float)。
⑥ double:64为表示,。如果double和float都无法达到想要的精度,可以使用BigDecimal类(定义时也可以加上D或者d,如:
double d1 = 167.7D;)。
⑦ char:char类型是一个单一的 16位 Unicode字符,char数据类型可以储存任何字符。
⑧ boolean:boolean数据类型表示一位的信息,取值仅有“false”和“true”,不能为“null”,默认取值为“false”;
四、 java中的类型转换
1. 自动转换
int a=10;
double b=a;
代码中int类型的a能够直接为double类型的b赋值,这种转换称为自动转换,完成自动转换需要满足以下两个条件:
① 目标类型能与源类型兼容,如,double 型兼容 int 型,但是 char 型不能兼容 int 型
② 目标类型大于源类型,如,double 类型长度为 8 字节,int 类型为 4 字节,因此 double 类型的变量里直接可以存放 int 类型的数据,但反过来就不可以了
2. 强制转换
double a=10.8;
int b=(int)a;
代码中通过在a前面加上(int)将double类型的a强制转换成了int类型的b,这种转换称为强制转换。(注意,如代码中的a为10.8但强制转换成int类型的b后的值为10,因此强制转换并不是四舍五入而是直接舍去,会导致部分数据的丢失!)。
五、java中的变量类型有哪些?
1.局部变量
① 局部变量声明在方法、构造方法或者语句块中。
② 局部变量在方法、构造方法、或者语句块被执行的时候创建,当它们执行完成后,变量将会被销毁。
③ 访问修饰符不能用于局部变量。
④ 局部变量只在声明它的方法、构造方法或者语句块中可见。
⑤ 局部变量是在栈上分配的。
⑥ 局部变量没有默认值,所以局部变量被声明后,必须经过初始化,才可以使用。
2.实例变量
① 实例变量声明在一个类中,但在方法、构造方法和语句块之外;
② 当一个对象被实例化之后,每个实例变量的值就跟着确定;
③ 实例变量在对象创建的时候创建,在对象被销毁的时候销毁;
④ 实例变量的值应该至少被一个方法、构造方法或者语句块引用,使得外部能够通过这些方式获取实例变量信息;
⑤ 实例变量可以声明在使用前或者使用后;
⑥ 访问修饰符可以修饰实例变量;
⑥ 实例变量对于类中的方法、构造方法或者语句块是可见的。一般情况下应该把实例变量设为私有。通过使用访问修饰符可以使实例变量对子类可见;
⑦ 实例变量具有默认值。数值型变量的默认值是0,布尔型变量的默认值是false,引用类型变量的默认值是null。变量的值可以在声明时指定,也可以在构造方法中指定;
⑧ 实例变量可以直接通过变量名访问。但在静态方法以及其他类中,就应该使用完全限定名:ObejectReference.VariableName。
3.类变量(静态变量)
① 类变量也称为静态变量,在类中以static关键字声明,但必须在方法构造方法和语句块之外。
② 无论一个类创建了多少个对象,类只拥有类变量的一份拷贝。
③ 静态变量除了被声明为常量外很少使用。常量是指声明为public/private,final和static类型的变量。常量初始化后不可改变。
④ 静态变量储存在静态存储区。经常被声明为常量,很少单独使用static声明变量。
⑤ 静态变量在程序开始时创建,在程序结束时销毁。
⑥ 与实例变量具有相似的可见性。但为了对类的使用者可见,大多数静态变量声明为public类型。
⑦ 默认值和实例变量相似。数值型变量默认值是0,布尔型默认值是false,引用类型默认值是null。变量的值可以在声明的时候指定,也可以在构造方法中指定。此外,静态变量还可以在静态语句块中初始化。
⑧ 静态变量可以通过:ClassName.VariableName的方式访问。
⑨ 类变量被声明为public static final类型时,类变量名称一般建议使用大写字母。如果静态变量不是public和final类型,其命名方式与实例变量以及局部变量的命名方式一致。
---------------------
(二)【基础知识梳理——JAVAse部分】修饰符
一、Java中有哪两类修饰符?
Java语言提供了很多修饰符,主要分为以下两类:
① 访问修饰符
② 非访问修饰符
二、什么是访问修饰符?
Java中,可以使用访问控制符来保护对类、变量、方法和构造方法的访问。Javav支持 4 种不同的访问权限:
① default (即缺省,什么也不写):在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。
② private :在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类)。
③ public :对所有类可见。使用对象:类、接口、变量、方法。
④ protected :对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类)。
我们可以通过以下表来说明访问权限:
二、什么是非访问修饰符?
为了实现一些其他的功能,Java也提供了许多非访问修饰符:
① static修饰符,用来修饰类方法和类变量,分别称为静态方法和静态变量,其中各自用法不同:
(1) 静态方法:static关键字用来声明独立于对象的静态方法。静态方法不能使用类的非静态变量。静态方法从参数列表得到数据,然后计算这些数据。
(2) 静态变量:static关键字用来声明独立于对象的静态变量,无论一个类实例化多少对象,它的静态变量只有一份拷贝。 静态变量也被称为类变量。局部变量不能 被声明为 static变量。
② final修饰符,用来修饰类、方法和变量,final修饰的类不能够被继承,修饰的方法不能被继承类重新定义,修饰的变量为常量,是不可修改的。
③ abstract修饰符,用来创建抽象类和抽象方法。
④ synchronized和 volatile修饰符,主要用于线程的编程。
---------------------
(三)【基础知识梳理——JAVAse部分】Java中面向对象相关知识
面向对象是一种程序设计思想,计算机程序的设计实质上就是将现实中的一些事物的特征抽离出来描述成一些计算机事件的过程,这种抽象的过程中,我们把具体的事物封装成一个一个的整体进行描述,使被描述的对象具备一些系统性、整体性的的行为和特征,我们把这种设计思想称为面向对象
一、Java面向对象有哪三大特征?
分别为继承、封装、多态;封装和继承几乎都是为多态服务的。
二、什么是继承?
继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类。继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
继承有如下些特征:
① 子类拥有父类非private的属性,方法。
② 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
③ 子类可以用自己的方式实现父类的方法。
④ Java的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如A类继承B类,B类继承C类,所以按照关系就是C类是B类的父类,B类是A类的父类,这是java继承区别于C++继承的一个特性。
⑤ 提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系)。
三、继承中子类应该怎么调用父类的构造器?
① 子类不能继承父类的构造器(构造方法或者构造函数),但是父类的构造器带有参数的,则必须在子类的构造器中显式地通过super关键字调用父类的构造器并配以适当的参数列表。
② 如果父类有无参构造器,则在子类的构造器中用super调用父类构造器不是必须的,如果没有使用super关键字,系统会自动调用父类的无参构造器。
四、什么是封装?
在面向对象程式设计方法中,封装是指一种将抽象性函式接口的实现细节部份包装、隐藏起来的方法。封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代码随机访问。要访问该类的代码和数据,必须通过严格的接口控制。封装最主要的功能在于我们能修改自己的实现代码,而不用修改那些调用我们代码的程序片段。适当的封装可以让程式码更容易理解与维护,也加强了程式码的安全性。
封装的优点:
① 良好的封装能够减少耦合。
② 类内部的结构可以*修改。
③ 可以对成员变量进行更精确的控制。
④ 隐藏信息,实现细节。
五、怎么实现封装?
① 修改属性的可见性来限制对属性的访问(一般限制为private)。
② 对每个值属性提供对外的公共方法访问,也就是创建一对赋取值方法(set和get方法),用于对私有属性的读取和设置。
六、什么是多态?
多态是同一个行为具有多个不同表现形式或形态的能力;就是同一个接口,使用不同的实例而执行不同操作。如,我们F1这个快捷键在不同应用呈现不同的功能。
七、多态有什么优点?
① 可替换性:多态对已存在代码具有可替换性。
② 可扩充性:多态对代码具有可扩充性。增加新的子类不影响已存在类的多态性、继承性,以及其他特性的运行和操作。实际上新加子类更容易获得多态功能。
③ 接口性:多态是超类通过方法签名,向子类提供了一个共同接口,由子类来完善或者覆盖它而实现的。
④ 灵活性:它在应用中体现了灵活多样的操作,提高了使用效率。
简化性多态简化对应用软件的代码编写和修改过程,尤其在处理大量对象的运算和操作时,这个特点尤为突出和重要。
八、多态存在的条件是什么?
① 要有继承;
② 要有重写;
③ 父类引用指向子类对象。
(难点)九、多态的方法调用的优先级是怎么样的?
优先级由高到低依次为:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O),其中super表示父类
这里我找了一个经典的多态的练习题:
class A ...{
public String show(D obj)...{
return ("A and D");
}
public String show(A obj)...{
return ("A and A");
}
}
class B extends A...{
public String show(B obj)...{
return ("B and B");
}
public String show(A obj)...{
return ("B and A");
}
}
class C extends B...{}
class D extends B...{}
A a1 = new A();
A a2 = new B();
B b = new B();
C c = new C();
D d = new D();
System.out.println(a1.show(b)); ①
System.out.println(a1.show(c)); ②
System.out.println(a1.show(d)); ③
System.out.println(a2.show(b)); ④
System.out.println(a2.show(c)); ⑤
System.out.println(a2.show(d)); ⑥
System.out.println(b.show(b)); ⑦
System.out.println(b.show(c)); ⑧
System.out.println(b.show(d)); ⑨
那么运算结果会是怎么样呢?
① A and A
② A and A
③ A and D
④ B and A
⑤ B and A
⑥ A and D
⑦ B and B
⑧ B and B
⑨ A and D
为什么会是这个样子呢?
①不用怎么说,当a1.show()方法里面参数时,根据优先级最终会调用A中的show(A obj)方法,因此输出“A and A”。
④可能难以理解,这里面我们先来介绍重写和重载。如果在子类中定义某方法与其父类有相同的名称和参数,我们说该方法被重写。子类的对象使用这个方法时,将调用子类中的定义,对它而言,父类中的定义如同被“屏蔽”了;如果在一个类中定义了多个同名的方法,它们或有不同的参数个数或有不同的参数类型,则称为方法的重载。首先看到④中a2.show(b)因为a的类型是A,b的类型是B,因此我们找到A里面的方法,但是我们这时是找不到show(B obj),因此根据优先级我们在A的父类里面找,但是A是没有父类的,因此我们把B的父类作为参数(即A),因此我们调用A中的show(A obj)方法,但是我们发现a2引用的是类B的一个对象,并且B中对show(A obj)的方法进行了重写,因此我们最终调用的是B中的show(A obj)方法,其他的同理可以得到。
十、重写和重载有什么区别?
重写:重写是子类对父类的允许访问的方法的实现过程进行重新编写,返回值和形参都不能改变。即外壳不变,核心重写!重写的好处在于子类可以根据需要,定义特定于自己的行为。也就是说子类能够根据需要实现父类的方法。重写方法不能抛出新的检查异常或者比被重写方法申明更加宽泛的异常。
重载:重载是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。最常用的地方就是构造器的重载。
十一、什么是接口,接口有什么作用?
接口,在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。除非实现接口的类是抽象类,否则该类要定义接口中的所有方法。
接口的作用:
1、重要性:在Java语言中, abstract class 和interface是支持抽象类定义的两种机制。正是由于这两种机制的存在,才赋予了Java强大的面向对象能力。
2、简单、规范性:如果一个项目比较庞大,那么就需要一个能理清所有业务的架构师来定义一些主要的接口,这些接口不仅告诉开发人员你需要实现那些业务,而且也将命名规范限制住了(防止一些开发人员随便命名导致别的程序员无法看明白)。
3、维护、拓展性:比如你要做一个画板程序,其中里面有一个面板类,主要负责绘画功能,然后你就这样定义了这个类。可是在不久将来,你突然发现这个类满足不了你了,然后你又要重新设计这个类,更糟糕是你可能要放弃这个类,那么其他地方可能有引用他,这样修改起来很麻烦。如果你一开始定义一个接口,把绘制功能放在接口里,然后定义类时实现这个接口,然后你只要用这个接口去引用实现它的类就行了,以后要换的话只不过是引用另一个类而已,这样就达到维护、拓展的方便性。
---------------------
(四)【基础知识梳理——JAVAse部分】Java中的集合框架
Java的集合类主要由两个接口派生而出:Collection和Map,Collection和Map是Java集合框架的根接口,这两个接口又包含了一些接口或实现类。
这是我们常用的一些集合,因此我们也主要围绕这些来讲(其中黄色为最为重要的)。
一、什么是Collection接口?
① Collection接口有两个主要的子接口List和Set,注意Map不是Collection的子接口。
② Collection中可以存储的元素间无序,可以重复组各自独立的元素, 即其内的每个位置仅持有一个元素,同时允许有多个 null元素对象。
二、什么是List接口?
List中存储的元素实现类排序,而且可以重复的存储相关元素。同时List接口又有两个常用的实现类ArrayList和LinkedList。
三、ArrayList和LinkedList的有什么区别?
1. ArrayList:
可以这样理解ArrayList就是基于数组的一个线性表,只不过数组的长度可以动态改变而已。
特点:类似数组的形式进行存储,因此它的随机访问速度极快。
缺点:不适合于在线性表中间需要频繁进行插入和删除操作。因为每次插入和删除都需要移动数组中的元素。
需要注意的点:
① 如果在初始化ArrayList的时候没有指定初始化长度的话,默认的长度为10.
② ArrayList在增加新元素的时候如果超过了原始的容量的话,ArrayList扩容ensureCapacity的方案为“原始容量 *3/2+1"。
③ ArrayList是线程不安全的,在多线程的情况下不要使用。(Vector可以代替ArrayList在多线程情况下使用)。
④ ArrayList的遍历方法:
List<String> list=new ArrayList<String>();
list.add("Hello");
list.add("World");
list.add("HAHAHAHA");
//第一种遍历方法使用foreach遍历List
for (String str : list) { //也可以改写for(int i=0;i<list.size();i++)这种形式
System.out.println(str);
}
//第二种遍历,把链表变为数组相关的内容进行遍历
String[] strArray=new String[list.size()];
list.toArray(strArray);
for(int i=0;i<strArray.length;i++) //这里也可以改写为foreach(String str:strArray)这种形式
{
System.out.println(strArray[i]);
}
//第三种遍历 使用迭代器进行相关遍历
Iterator<String> ite=list.iterator();
while(ite.hasNext()){
System.out.println(ite.next());
}
2.LinkedList:
可以这样理解LinkedList就是一种双向循环链表的链式线性表,只不过存储的结构使用的是链式表而已。
特点为: 适合于在链表中间需要频繁进行插入和删除操作。
缺点: 随机访问速度较慢。查找一个元素需要从头开始一个一个的找。速度你懂的。
需要注意的点:
① LinkedList和ArrayList的区别和联系
② LinkedList的内部实现。(可以自己在网上去查看,往后可能我也会写在博客上)
③ LinkedList不是线程安全的:如果在对线程下面访问可以自己重写LinkedList,然后在需要同步的方法上面加上同步关键字 synchronized。
④ LinkedList的遍历方法 :
List<String> list=new LinkedList<String>();
list.add("Hello");
list.add("World");
list.add("Hi");
//LinkedList遍历的第一种方式使用数组的方式
String[] strArray=new String[list.size()];
list.toArray(strArray);
for(String str:strArray){
System.out.println(str);
}
//LinkedList遍历的第二种方式
for(String str:list){
System.out.println(str);
}
五、什么是Set接口
Set接口也是Collection接口的一个常用子接口。Set接口区别于List接口的特点在于:
Set中的元素实现了不重复,有点象集合的概念,无序,不允许有重复的元素,最多允许有一个null元素对象。需要注意的是:虽然Set中元素没有顺序,但是元素在set中的位置是有由该元素的HashCode决定的,其具体位置其实是固定的。(Set集合中的重复规定和hashcode与equals方法直接相关。)
六、什么是HashSet接口?
HashSet的底层是基于HashMap来实现的,需要注意下面几个特点:
① HashSet中存放null值:HashSet中时允许出入null值的,但是在HashSet中仅仅能够存入一个null值哦。
② HashSet中存储元素的位置是固定的:HashSet中存储的元素的是无序的,但是由于HashSet底层是基于Hash算法实现 的,使用了hashcode,所以HashSet中相应的元素的位置是固定的。
七、什么是Map接口?
说到Map接口的话大家也许在熟悉不过了。Map接口实现的是一组Key-Value的键值对的组合。 Map中的每个成员方法由一个关键字(key)和一个值(value)构成。Map接口不直接继承于Collection接口,因为它包装的是一组成对的“键-值”对象的集合,而且在Map接口的集合中也不能有重复的key出现,因为每个键只能与一个成员元素相对应。在我们的日常的开发项目中,我们无时无刻不在使用着Map接口及其实现类。Map有两种比较常用的实现:HashMap和TreeMap。
八、HashMap和HashTbale的区别是什么?
HashMap和Hashtable都实现了Map接口,但决定用哪一个之前先要弄清楚它们之间的分别。主要的区别有:线程安全性,同步,以及速度:
① HashMap几乎可以等价于Hashtable,除了HashMap是synchronized的,并可以接受null(HashMap可以接受为null的 键值(key) 和值(value),而Hashtable则不行)。
② HashMap是非synchronized,而Hashtable是synchronized,这意味着Hashtable是线程安全的,多个线程可以共享一 个Hashtable;而如果没有正确的同步的话,多个线程是不能共享HashMap的。Java 5提供了ConcurrentHashMap,它是 HashTable的替代,比HashTable的扩展性更好。
③ 另一个区别是HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以 当有其它线程改变了HashMap的结构(增加或者移除元素),将会抛出ConcurrentModificationException,但迭代器本身 的remove()方法移除元素则不会抛出ConcurrentModificationException异常。但这并不是一个一定发生的行为,要看JVM。 这条同样也是Enumeration和Iterator的区别。
④ 由于Hashtable是线程安全的也是synchronized,所以在单线程环境下它比HashMap要慢。如果你不需要同步,只需要 单一线程,那么使用HashMap性能要好过Hashtable。
⑤ HashMap不能保证随着时间的推移Map中的元素次序是不变的。
---------------------
作者:会Java的石油工人
来源:CSDN