Java基础重点知识
目录
- 面向对象的三大特性是什么?
- 重载和重写有什么区别?
- 多态的实现机制是什么?
- 是否可以继承String类
- 接口和抽象类的区别?
- 内部类的介绍
- equals()和==区别
- 静态变量和实例变量的区别
- Integer与int的区别
- String,StringBuilder,StringBuffer
- java程序初始化的顺序是什么样的.
- Class.forName和classloader区别
- Collection和Collections有什么区别
- List和Set,map有什么区别?List,Set,Map是否继承自Collection接口?
- ArrayList和LinkList有何区别?
- ArrayList中的元素如何删除?
- iterator和listIterator的区别
- Comparable和Comparator接口有什么区别?
面向对象的三大特性是什么?
- 继承
继承就是子类继承父类的特征和行为,似的子类对象(实例)具有父类实例和方法,或子类从父类继承方法,使用、子类具有父类相同的行为。Java的继承是单继承。 - 封装
封装就是把同一类事物的属性和方法归到同一个类中,方便使用。 封装可以被认为一个保护屏障,防止该类的代码和数据被外部类定义的代码随机访问。封装最主要的功能在于我们能修改自己的代码实现,而不弄修改那些调用我们代码的程序片段。 - 多态
多态是同一个行为具有多个不同表现形式或形态的能力。
多态是同一个接口,使用不同的实例而执行不同的操作。产生的条件继承、重写、父类引用指向子类对象。
重载和重写有什么区别?
- 重写(Override)
重写是子类对父类的允许访问的方法的实现过程进行重新编写,返回值和形参都不能改变,即外壳不变,核心重写!
重写的好处在于子类可以根据需要,定义特定于自己的行为,也就是说子类能够根据需要实现父类的方法。 - 重载(Overload)
重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。
每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。
最常用的地方就是构造器的重载。
总结一下:
(1)方法重载是一个类中定义了多个方法名相同,而他们的参数的数量不同或数量相同而类型和次序不同,则称为方法的重载(Overloading)。
(2)方法重写是在子类存在方法与父类的方法的名字相同,而且参数的个数与类型一样,返回值也一样的方法,就称为重写(Overriding)。
(3)方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现。
多态的实现机制是什么?
封装和继承几乎都是为多态而准备的。
多态是同一个行为具有多个不同表现形式或形态的能力。
多态是同一个接口,使用不同的实例而执行不同操作。
多态是面向对象编程语言的重要特性,它允许基类的指针或引用指向派生类的对象,而在具体访问时实现方法的动态绑定。
多态存在的三个必要条件:
继承(核心)
重写
父类引用指向子类对象。
是否可以继承String类
不可以,String是final类型的,不能继承。
接口和抽象类的区别?
- 共同点:
都是上层的抽象层
都不能被实例化
都不能包含抽象方法,这些抽象的方法用于描述具备的功能 - 区别
- 在抽象类中可以写非抽象方法,从而避免砸子类中重复书写它们。这样可以提高代码的复用性,这是抽象类的优势接口中只能有抽象的方法
- 一个类只能继承一个直接父类,这个父类可以是具体的类也可是抽象类,但是一个类可以实现多个接口,接口的设计具有更大的可扩展性,而抽象类设计必须十分谨慎。
*抽象界别(从到到低)接口>抽象类>实现类
*接口的设计目的,是对累的行为进行约束,侧重于动作,而抽象类的设计目的,是代码复用。
内部类的介绍
放在一个类的内部的类我们叫做内部类
- 作用
- 内部类可以很好的实现隐藏
- 内部类拥有外部类的所有元素访问权限
- 可以实现多重继承
equals()和==区别
*equals()和"=="操作用于对象的比较,检查两对象的相等性,但是他们的主要区别在于强者是方法,后者是操作符。
- 一般使用==比较原生类型如:boolean、int、char等等,使用equals()比较对象
- 一般使用==比较指向相同的对象 返回true,equals()的返回结果依赖于具体业务实现,一般重写equals方法时,同样重写hashcode方法,默认的equals方法实现是与""操作符一样的。
- 字符串的对比使用equals()代替==操作符。
静态变量和实例变量的区别
解释:
- 在语法定义上的区别:在静态变量前要加static关键字,二实例变量(成员变量)前则不加
*在程序运行时的区别:实例变量属于某个对象的属性,必须创建了实例对象,其中的实例变量才会分配空间,才能使用这个实例变量。
静态变量不属于某个实例对象,而是属于类,所以称为类变量,只要程序加载了类的字节码,不用创建任何实例对象,静态变量就会被分配空间,只分配一次,静态变量就可以被使用了。总之,实例变量必须穿件对象后才可以通过这个对象来使用,静态变量则可以直接使用类名来引用。
参考代码
public class VariantTest {
public static int staticVar=0;
public int instanceVar=0;
public VariantTest() {
staticVar++;
instanceVar++;
System.out.println("staticVar:"+staticVar+"--instanceVar:"+instanceVar);
}
public static void main(String [] args)
{
VariantTest var=new VariantTest();
VariantTest var1=new VariantTest();
VariantTest var2=new VariantTest();
VariantTest va3=new VariantTest();
VariantTest var4=new VariantTest();
VariantTest va5=new VariantTest();
}
}
// 结果
/*
staticVar:1--instanceVar:1
staticVar:2--instanceVar:1
staticVar:3--instanceVar:1
staticVar:4--instanceVar:1
staticVar:5--instanceVar:1
staticVar:6--instanceVar:1
*/
Integer与int的区别
解释
- Integer是int提供的分装类,从java5开始引入了自动封装/拆箱机制,是的二中可以相互转换。而int是java的基本数据类型。
- Integer默认值是null,而int默认值是0
- Integer是对象,用一个引用指向这个对象,而int是基本类型,直接存储数值。
- Integer提供了好多鱼整数相关的操作方法,例如,讲一个字符串转换成整数等。
代码
public static void main(String [] args)
{
int i=128;
Integer i2=128;
Integer i3=new Integer(128);
System.out.println(i==i2); //Integer会自动拆箱为int,所以为true
System.out.println(i==i3);//true
Integer i4=127;
Integer i5=127;
System.out.println(i4==i5);//true
/*
* -128 and 127 (inclusive) as required by JLS
* Integer缓存换位是-128到127
* */
Integer i6=128;
Integer i7=128;
System.out.println(i6==i7);//false
/*
* i5 从缓存中直接返回
* i8 new了一个新对象
* */
Integer i8=new Integer(127);
System.out.println(i5==i8);//false
/*
* new了两个新对象
* */
Integer i9=new Integer(128);
Integer i10=new Integer(128);
System.out.println(i9==i10); //false
}
String,StringBuilder,StringBuffer
解释:
- 执行速度,StringBuilder>StringBuffer>String
String最慢的原因
String为字符串常量,而StringBuilder和StringBuffer均为字符串变量,即String对象一旦创建之后该对象是不可改变的,但后者的对象时变量,是可以改变的。 - 线程安全
在线程安全上, StringBuilder是线程不安全的,而StringBuffer是线程安全的。(StringBuffer中很多方法带有synchronized关键字)
总结
String: 适用于少量字符串操作。
StringBuilder:适用于单线程下载字符缓冲区进行大量的操作情况。
StringBuffer:适用多线程下在字符缓冲区进行大量的操作的情况。
代码
public static void main(String [] args)
{
String s1="233";
s1=s1+"www";
System.out.println(s1);
StringBuilder sbd=new StringBuilder();
sbd.append("StringBuilder").append("12").append("23").append(56);
System.out.println("StringBuilder:"+sbd.toString());
StringBuffer sbf=new StringBuffer();
sbf.append("StringBuffer").append("12").append("34").append("56");
System.out.println("StringBuffer:"+sbf.toString());
StringBuilder sb=new StringBuilder("1111122222");
StringBuffer sbffer=new StringBuffer("3333344444");
// 下面是验证线程安全的测试
// 循环创建多个线程,测试StringBuilder
for (int i=0;i<=100;i++)
{
new Thread(new MyThread(sb)).start();
}
// 循环创建多个线程,测试StringBuffer
for (int i = 0; i < 100; i++) {
new Thread(new MyThreadSbf(sbffer)).start();
}
}
private static class MyThread implements Runnable {
// 定义成员属性
private StringBuilder sb;
// 构造函数,参数初始化
private MyThread(StringBuilder sb) {
this.sb = sb;
}
public void run() {
// 循环可以增加出现线程不安全的情况
for (int i = 0; i < 100; i++) {
sb.reverse();
}
// 输出sb字符串(默认调用了toString())
System.out.println(sb);
}
}
private static class MyThreadSbf implements Runnable {
// 定义成员属性
private StringBuffer sb;
// 构造函数,参数初始化
private MyThreadSbf(StringBuffer sb) {
this.sb = sb;
}
public void run() {
// 循环可以增加出现线程不安全的情况发生
for (int i = 0; i < 100; i++) {
sb.reverse();
}
// 输出sb字符串(默认调用了toString())
System.out.println(sb);
}
}
/*
* 2121111212
2121111212
3333344444
3333344444
4444433333
3333344444
3333344444
* */
java程序初始化的顺序是什么样的.
解
- 静态对象(变量)优先于非静态对象(变量)初始化,其中静态对象(变量)只初始化一次,而非静态对象(变量)可能会初始化多次。
- 父类优先于子类进行初始化
- 按照成员变量的定义顺序进行初始化。即使变量定义散布于方法中,它们依然在任何方法(包括构造函数)被调用前先初始化。
总结:
父类静态字段初始化
父类静态代码块
子类静态字段初始化
子类静态代码块初始化
父类普通字段初始化
父类构造代码块
父类构造函数
子类普通字段初始化
子类构造代码块
子类构造函数
static字段、代码块的执行顺序优先于非static字段、代码块。这是因为在静态域是属于类的,在类加载后就一直存在:而普通类需要创建对象才能访问。而在创建对象时,需要先加载父类,然后再加载子类,因此父类的静态字段初始化和静态代码块执行先于子类。
// 测试Java程序初始化的顺序
public class CodeBlockTest {
public static void main(String[] args) {
Child child = new Child();
}
}
class Father {
public static String fatherStr1 = "fatherStr1(静态字段初始化值)";
public String fatherStr2 = "fatherStr2(字段初始化值)";
static {
System.out.println("父类静态代码块:" + fatherStr1 +",静态变量已经初始化");
fatherStr1 = "fatherStr1(静态代码块赋值)";
}
{
System.out.println("父类构造代码块fatherStr1:" + fatherStr1);
System.out.println("父类构造代码块fatherStr2:" + fatherStr2);
fatherStr2 = "fatherStr2(构造代码块赋值)";
}
public Father() {
System.out.println("父类构造函数:" + fatherStr2);
fatherStr2 = "fatherStr2(构造函数赋值)";
}
}
class Child extends Father {
public static String childStr1 = "childStr1(静态字段初始化值)";
public String childStr2 = "childStr2(字段初始化值)";
static {
System.out.println("子类静态代码块:" + childStr1 + ",静态变量已经初始化");
childStr1 = "childStr1(静态代码块赋值)";
}
{
System.out.println("子类构造代码块childStr1:" + childStr1);
System.out.println("子类构造代码块childStr2:" + childStr2);
childStr2 = "childStr2(构造代码块赋值)";
}
public Child() {
System.out.println("子类构造函数:" + childStr2);
childStr2 = "childStr2(构造函数赋值)";
}
}
/*父类静态代码块:fatherStr1(静态字段初始化值),静态变量已经初始化
子类静态代码块:childStr1(静态字段初始化值),静态变量已经初始化
父类构造代码块fatherStr1:fatherStr1(静态代码块赋值)
父类构造代码块fatherStr2:fatherStr2(字段初始化值)
父类构造函数:fatherStr2(构造代码块赋值)
子类构造代码块childStr1:childStr1(静态代码块赋值)
子类构造代码块childStr2:childStr2(字段初始化值)
子类构造函数:childStr2(构造代码块赋值)*/
Class.forName和classloader区别
- 相同点:
java中class.forName() 和classLoader都可用来对类进行加载 - 不同点:
Class.forName除了该类的.class 文件加载到jvm中之外,还会对类进行解释,执行类中的static块
而classLoader只干一件事情,就是将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块
Class.forName(name,initialize,loader)带参函数也可以控制是否加载static块。并且只有调用了newInstace()方法采用调用构造函数,来创建累的对象。
Collection和Collections有什么区别
- java.util.Collection 是一个集合接口。它提供了对集合对象进行基本操作的通用接口。Collection接口在Java类库中有很多的具体实现。Collection接口的意义是为各种具体的集合提供最大化的统一操作形式。
- List、set、Queue接口都继承Collection。直接实现该接口的类只有AbstractCollection类,该类也只有一个抽象类,提供了对集合类操作的一些基本实现。List和Set的具体实现类基本上都直接或间接的继承了该类。
java.util.Collections是一个包装类,它包含有各种集合操作的静态方法(对结合搜索、排序、线程安全化等)大多数方法都是用来处理线性表的。此类不能实例化,可以把Collections是一个工具类。
List和Set,map有什么区别?List,Set,Map是否继承自Collection接口?
List
- 可以允许重复的对象
- 可以插入多个null值
- 是一个有序容器,保持了每个元素的插入顺序,输出的顺序就是插入的顺序。
- 常用的实现类有ArrayList、LinkedList和Vector。ArrayList最为流行,它提供了使用索引的任意访问,而LinkedList则对于经常需要从List中添加或者删除元素的场合更为合适。
Set
- 不允许重复对象
- 无序容器,你无法保证每个元素的存储顺序。ThreeSet通过Comparator或者Comparable维护了一个排序顺序。
- 只允许一个null元素
- Set接口最流行的几个实现类是HashSet、LinkedHashSet以及TreeSet
Map
- Map不是collection的子接口或者实现类。Map是一个接口。
- Map的每个Entry都保持有两个对象,也就是一个键一个值,Map不是。
- TreeMap也通过Comparator或者Comparable维护了一个排序顺序。
- Map里你可以拥有随意个null值但最多只能有一个null键。
- Map接口最流行的几个实现类是HashMap、LinkedHashMap、和TreeMap(HashMap、TreeMap最常用)。
List,Set,Map是否继承自Collection接口?
List、set是继承自Collection接口,Map不是。
代码
public interface List<E> extends Collection<E>
public interface Set<E> extends Collection<E>
public interface Map<K,V>
public static void main(String [] args)
{
List<java.lang.String> list=new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
list.add("D");
list.add("D");
list.add(null);
list.add(null);
System.out.println("list:"+list);
Set<String>set=new HashSet<>();
set.add("A");
set.add("B");
set.add("C");
set.add("D");
set.add("A");
set.add("null");
set.add("null");
System.out.println("set:"+set);
Map<String,String> map=new HashMap<>();
map.put("A","A");
map.put("A","B");
map.put("B","C");
map.put("C","D");
System.out.println("map:"+map);
}
/* list:[A, B, C, D, D, null, null]
set:[A, B, C, D, null]
map:{A=B, B=C, C=D}*/
ArrayList和LinkList有何区别?
- ArrayList是实现了基础动态数组的数据结构,而LinkedList是基于链表的数据结构
- 对于随机访问的get和set,ArrayList优先于LinkedList,因为LinkedList要移动指针
- 对于添加和删除操作的add和remove,一般大家都会说LinkedList要比ArrayList快,因为ArrayList要移动数据。
ArrayList中的元素如何删除?
- 使用iterator(迭代器)
public static void main(String [] args)
{
List<String> list=new ArrayList<>();
list.add("北京");
list.add("上海");
list.add("重庆");
list.add("广东");
list.add("重庆");
list.add("广东");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext())
{
String item=iterator.next();
if(item.equals("广东"))
{
iterator.remove();
}
}
System.out.println(list);
}
iterator和listIterator的区别
- listIterator 继承自iterator
- listIterator比iterator强大,listIterator提供了很多iterator没有的方法.如(add、nextIndex()、set、hasPrevious等)
public static void main(String [] args)
{
List<String> list=new ArrayList<>();
list.add("beijing");
list.add("shanghai");
list.add("guangzhou");
list.add("tangshan");
Iterator<String> it = list.iterator();
while (it.hasNext())
{
String item=it.next();
if(item.equals("shanghai"))
{
it.remove();
}
}
System.out.println(it);
Iterator<String> it2= list.iterator();
while (it2.hasNext())
{
System.out.println("Iterator:"+it2.next());
}
List<String> list2=new ArrayList<>();
list2.add("beijing");
list2.add("shanghai");
list2.add("guangzhou");
list2.add("tangshan");
ListIterator<String> stringListIterator = list2.listIterator();
String first=stringListIterator.next();
System.out.println("first:"+first);
stringListIterator.add("香港");
//遍历list方法1:
System.out.println("遍历list方法1");
for (String str:list2)
{
System.out.println(str);
}
//遍历list方法2:
System.out.println("遍历list方法2");
while (stringListIterator.hasNext())
{
System.out.println("下一个元素索引:"+stringListIterator.nextIndex());
System.out.println("下一个元素:"+stringListIterator.next());
}
}
Comparable和Comparator接口有什么区别?
相同点
Comparable和Comparator都是用来实现集合中元素的比较、排序的。
不同点
- 接口定义的方法不同
Comparable接口里面的方法是public int compareTo(T o),在java.lang包下
Comparator接口里面的方法是 int compare(T O1,T O2) 在java.util包下 - Comparable是在集合内部定义的方法实现排序,Comparator是在集合外部实现的排序。
- Comparator比Comparable 要灵活
Comparable
final int STUDENT_NUM=4;
Student[] allStudents=new Student[STUDENT_NUM];
allStudents[0]=new Student("0001","a");
allStudents[1]=new Student("0002","b");
allStudents[2]=new Student("0004","d");
allStudents[3]=new Student("0003","c");
for (int i=0;i<allStudents.length;i++)
{
allStudents[i].setAge(i*10);
allStudents[i].setScore(99-i*1.5);
}
Arrays.sort(allStudents);
System.out.println("学号"+"\t姓名"+"\t年龄"+"\t成绩");
for (int i=0;i<allStudents.length;i++)
{
System.out.println(allStudents[i]);
}
public Student(String ID, String name, Integer age, Double score) {
this.ID = ID;
this.name = name;
this.age = age;
this.score = score;
}
public String getID() {
return ID;
}
public void setID(String ID) {
this.ID = ID;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Double getScore() {
return score;
}
public void setScore(Double score) {
this.score = score;
}
@Override
public int compareTo(Student o) {
return this.ID.compareTo(o.ID);
}
@Override public String toString() {
return "Student{" + "ID='" + ID + '\'' + ", name='" + name + '\'' + ", age=" + age + ", score=" + score + '}';
}
}
Comparator
public class ComparatorOrder {
public static void main(String [] args)
{
final int STUDENT_NUM=4;
Student[] allStudents=new Student[STUDENT_NUM];
allStudents[0]=new Student("0001","a");
allStudents[1]=new Student("0002","b");
allStudents[2]=new Student("0004","d");
allStudents[3]=new Student("0003","c");
for (int i=0;i<allStudents.length;i++)
{
allStudents[i].setAge(i*10);
allStudents[i].setScore(99-i*1.5);
}
System.out.println("按姓名进行降序");
Arrays.sort(allStudents,new ComparatorWithNameDown());
for (int i = 0; i < allStudents.length; i++) {
System.out.print(allStudents[i]);
}
System.out.println();
System.out.println("按姓名进行升序");
Arrays.sort(allStudents,new ComparatorWithOrder());
for (int i = 0; i < allStudents.length; i++) {
System.out.print(allStudents[i]);
}
System.out.println();
System.out.println("分数进行升序");
Arrays.sort(allStudents,new ComparatorWithScoreUp());
for (int i = 0; i < allStudents.length; i++) {
System.out.print(allStudents[i]);
}
System.out.println();
System.out.println("按分数进行降序");
Arrays.sort(allStudents,new ComparatorWithScoreDown());
for (int i = 0; i < allStudents.length; i++) {
System.out.print(allStudents[i]);
}
}
}
public class ComparatorWithOrder implements Comparator<Student> {
// 按姓名进行升序排序
@Override
public int compare(Student o1, Student o2) {
return o1.getName().compareTo(o2.getName());
}
}
// 按姓名进行倒叙排序
class ComparatorWithNameDown implements Comparator<Student>
{
@Override
public int compare(Student o1, Student o2) {
return o2.getName().compareTo(o1.getName());
}
}
// 按分数进行降序排序
class ComparatorWithScoreDown implements Comparator<Student>
{
@Override
public int compare(Student o1, Student o2) {
return o2.getScore().compareTo(o1.getScore());
}
}
// 按分数进行升序排序
class ComparatorWithScoreUp implements Comparator<Student>
{
@Override
public int compare(Student o1, Student o2) {
return o1.getScore().compareTo(o2.getScore());
}
}