JavaSE基本概念总结

欢迎访问:我的个人网站

JavaSE基础知识总结

JVM, JRE, JDK简述

  • JDK为Java开发工具包,包含所有开发中常用的工具(编译器,文档生成工具),通过JDK才可以进行程序的编译工作。包含JRE以及JVM。
  • JRE为Java程序的运行环境,运行的为编译之后的.class文件,JRE包含Java运行过程中所需要的类库以及Java命令。他只是一个运行环境,不能进行编译工作。
  • JVM为Java虚拟机,它运行翻译后的.class文件,JVM是实现Java跨平台的基础,同一份.class文件在不同平台上的JVM会被翻译为不同的指令以适应运行平台。

基本数据类型以及其包装类

  • byte----------------1byte-------------Byte
  • short---------------2bytes-------------Short
  • int------------------4bytes-------------Integer
  • long----------------8bytes-------------Long
  • float---------------4bytes--------------Float
  • double------------8bytes--------------Double
  • char---------------2bytes--------------Character
  • boolean-----------1bytes--------------Boolean

面向对象三大特性:封装,继承,多态

封装: 将一类事物相关的属性及其所具有的操作聚合到一起形成一个整体,达到与其他类别的事物隔离的目的,在Java中封装往往指的是将一类事物封装为一个独立的类(Class),每个类是一个相对独立的个体,可以在此基础上由一个类衍生出它具体的多个实例。在进行封装之后,可以将这个类理解为一个黑盒,外界只需要关系其暴露在外的操作即可而无需关系其内部如何实现。

继承: 指的是一个类可以通过某种方式具有另一个类的属性以及操作。这个过程即称为继承。在Java中使用extends关键字为类建立继承关系。在面向对象中,继承往往发生在具有某种联系的某些类之间,通过继承使得类具有很好的扩展性重用性及维护性。需要注意的是,Java中针对两个直接的类仅能建立单继承关系,但是可以进行多重继承。且子类无法继承父类中使用private修饰的属性以及方法,亦不能重写父类中的final方法。如果父类被final修饰,那么该类无法被继承。

多态: 允许一个父类的引用指向不同的子类实例,不同子类实例则通过重写进行不同形式的实现。因为多态的存在,使得程序可以在运行期间再确认真正使用要使用的子类实例,继而再确认要使用的功能。

重载,重写

重载: 发生在一个类里面,指的是一个类中对一个方法多种形式的实现,如果要对方法进行重载,需要满足以下的条件:

  • 方法名称必须完全相同
  • 针对不同形式的重载,需要有不同的形参,此处的不同指的是:具有不同的参数类型,参数顺序,参数个数。

在满足已上条件之后,针对不同形式的方法重载,可以具有不同的访问权限修饰符,返回值类型并可以抛出不同的异常类型。

重写(覆盖): 发生直接或间接的子父类中,表示在某些情况下,子类对父类提供的方法进行再实现的操作。如果要对父类中的方法进行重写操作,需要满足以下几个条件:

  • 被重写的方法名称必须与原方法相同。
  • 被重写方法必须具有与原方法相同的形参列表
  • 重写方法允许修改访问权限修饰符,但是重写后的访问权限修饰符范围必须大于等于原方法。
  • 重写后的方法返回值类型要与原方法相同。
  • 重写后的方法抛出的异常类型必须小于等于父类的异常。
  • 被重写的方法必须为非private修饰的。

访问权限修饰符范围:
具有四种访问权限修饰符,由大到小范围如下:
public :在本项目内均可以访问。
protected :在同一包内, 本类及本类的子类可以访问。该修饰符无法修饰类
default :同一包可见。
private:仅本类可见。

成员变量与局部变量的区别

  • 成员变量指的是在类中定义的变量,是属于某个实例的(在未使用static修饰的时候)。局部变量指的是在方法中定义的变量,作用于方法。
  • 成员变量可以被访问权限修饰符以及static修饰,但是局部变量不可以。当成员变量使用static修饰的时候,所有实例将共享该变量。
  • 成员变量以及局部变量均可以被final修饰。
  • 成员变量存在于堆中,局部变量存在于栈中。
  • 成员变量生命周期与其所属的实例相同。局部变量与其所在的方法生命周期相同。

静态方法与实例方法的区别

  • 静态方法也称为类方法,不被某个具体的实例所拥有,所有该类的实例在调用该方法的时候,使用的是同一个。且静态方法中仅能够使用该类中的静态变量以及静态方法。
  • 实例方法可以访问静态变量,静态方法以及普通方法。

String, StringBuffer, StringBuilder异同

以上三个均可以对字符串进行操作,但是存在几点差异:
String相较于StringBuffer以及StringBuilder :
String类被final关键字修饰,并且其底层使用的byte[]数组也是被final修饰的,这意味着使用String建立的一个字符串对象是不可变的,所以日常使用String类进行的字符串增删操作实际上是将引用指向了一个新的实例。因为它是不可变的,因此可以保证线程安全。
StringBuffer相较于StringBuilder:
二者均继承至AbstractStringBuilder,并且操作实际上是配合调用AbstractStringBuilder父类中的方法完成初始化工作的。AbstractStringBuilder类未被final修饰且底层使用的byte[]数组也未被final形式,这意味着这两个类是可
变的。且初始化数组长度为16,如果初始化的时候指定了字符串,则在字符串长度的基础上再次增加16个长度。

//二者均继承自AbstractStringBuilder
public final class StringBuffer extends AbstractStringBuilder implements java.io.Serializable, Comparable<StringBuffer>, CharSequence{}
public final class StringBuilder extends AbstractStringBuilder implements java.io.Serializable,Comparable<StringBuilder>, CharSequence {}

//二者均调用了父类的方法完成初始化工作, 以下为摘抄的部分源码  
public StringBuilder() {super(16);}
public StringBuilder(String str) {super(str.length() + 16);append(str);}
public StringBuffer() {super(16);}
public StringBuffer(String str) {super(str.length() + 16);append(str);}

StringBuilder是线程不安全的。而StringBuffer则针对各项操作使用synchronized进行了加锁操作以保证线程安全,可以从源码中看到这一点:

//未进行加锁操作  
public StringBuilder append(String str) {  
    super.append(str);  
	return this;
}

//进行了加锁操作
public synchronized StringBuffer append(String str) {  
 toStringCache = null;  
 super.append(str);  
 return this;
}

因此,可以根据不同环境进行选择,如果针对一些较少的字符串操作行为,可以直接使用String类进行处理。如果多线程环境建议使用StringBuffer保证数据一致,否则可以使用StringBuilder,因为其未进行加锁操作,因此在性能上相较于StringBuffer会有些许提升。

自动拆装箱

于JDK5时候引入的功能,为了方便进行基本数据类型与包装数据类型之间的转换,自动拆装箱可以代替JDK5之前手动进行的数据转换操作,减少编码量。
装箱: 将基本数据类型转换为其对应的包装数据类型
**拆箱:**将包装数据类型转换为其对应的基本数据类型

	int a = new Integer(1);//包装类->基本数据类型
	Integer b = 1;//基本数据类型->包装类

读取键盘输入常用方式

Scanner sc = new Scanner(System.in);
System.out.println(sc.next());//接收一行字符串数据并输出

BufferedReader sr = new BufferedReader(new InputStreamReader(System.in));
System,out.println(sr.readLine());//接收一行字符串数据并输出

System.out.println(System.console().readLine());//接收一行字符串数据并输出, 仅适用于具有控制台的情况

==与equals

用来比较是否相同,针对不同的比较对象有不同的策略:

  • 针对“==”而言:
    • 如果比较的对象为基本数据类型:比较二者的值是否相同。
    • 如果比较的对象为对象类型:比较二者的内存地址是否相同。
  • 针对equals而言:
    • 在未重写该方法的时候,比较的规则与“==”相同。
    • 在重写该方法只会,按照重写的规则进行。String类重写了equals方法,因此调用该方法的时候,比较的是字符串是否相同而非其地址。

equals与hashCode

  • equals在未被重写的时候用于比较两个对象的值或内存地址是否一致,得到结果是依靠比较两个对象而言的。
  • hashCode是针对一个对象得出散列码,这个过程实际上是经过一个散列函数得到的。再比较散列码以确认两个对象是否相同。在一定情况下,该散列函数不是完全可靠的,有几率会发生碰撞,造成两个不同对象得出相同散列码的问题。
  • 结合以上两点可以知道,当equals返回结果为真的时候,两个对象的散列码值也一定相同。
  • 当两个对象得到的散列码相同时,这两个对象不一定相同(发生了碰撞)。

final

作为Java中的一个关键字,常用来修饰类,变量,方法。意为最终的,在不同情况下有不同的含义:

  • 修饰类:当一个类被final所修饰的时候,表示这个类为最终类,此时无法再被其他类所继承。并且该类中的所有方法都将被隐式的修饰为final类型。
  • 修饰方法:当一个方法被final修饰的时候,意味着该方法无法再次被重写。类中private的方法实际上也会被隐式修饰为final类型。
  • 修饰变量:将一个变量修饰为常量,此时该变量在赋值之后则不可变。如果为非static的,赋值工作可以在:变量声明时,构造代码块中,构造函数中进行。如果为static类型的,则只可以在声明时,静态代码块中赋值。

类初始化顺序

  • 一个类中: 静态代码块->构造代码块->实例化时候所使用的构造函数
  • 父子类中,当实例化一个子类的时候将执行: 父类静态代码块->子类静态代码块->父类构造代码块->父类构造函数->子类构造代码块->子类构造函数。

String a = “a” 与 String b = new String(“a”)

  • 首先则二者的内存地址是不同的, String a=“a” 是存在于字符串常量池的,在放入字符串常量池之前会检查其中是否已经存在了“a”,如果字符串常量池不存在,则放入字符串“a”。最后把a的引用指向“a”;
  • String b = new String(“a”); 是存在于堆中的,表示在堆中开辟一块内存空间并存储这个字符串,然后将b的引用指向这块空间。
  • 可以使用intern()方法返回当前字符串在字符串常量池的引用。如果常量池没有该字符串,则将该字符串添加到常量池并返回引用。
   
   String s1 = "abc";  
   String s2 = new String("abc");  
   String s3 = new String("abc");  
   String s4 = s2.intern();//返回常量池中值为"abc"的引用  
 
   System.out.println(s1==s2);	//s1在常量池,s2在堆,地址不同  ->false
   System.out.println(s2==s3); //s2,s3 都在堆中,但是是不同对象,所在的内存地址空间也不相同 ->false
   System.out.println(s1==s4); //常量池中“abc”被s1与s4同时指向。为真 ->true

翻转字符串

AbstractStringBuilder中提供了对字符串翻转的方法reverse();可以查看其源码。 同样可以自己简单进行实现:

char[] arr = new char[]{'a', 'b', 'c', 'd'};  
int l = 0;  
int r = arr.length-1;  
while (l < r) {  
    arr[l]+=arr[r];  
    arr[r] = (char)(arr[l]-arr[r]);  
    arr[l++]-=arr[r--];  
}

Object对象方法概述:

//获取当前对象的Class对象
public final native Class<?> getClass();

//返回当前对象的相关信息,格式:对象的类名@对象的hashCode的十六进制形式
public String toString() {}

//获取当前对象的哈希码值,一般在Hash表中需要用到。
public native int hashCode();

//获取当前对象的一个克隆,如果要一类对象支持克隆操作,那么该类需要实现Cloneable标识接口,且需要重写clone方法。并在方法体内调用返回 super.clone()。否则将抛出异常。
protected native Object clone() throws CloneNotSupportedException;

//将当前对象所处的线程进入休眠状态,该方法提供了三种不同的重载形式,具有不同的形参列表
public final void wait() throws InterruptedException {}

//唤醒一个在此对象监视器上等待的线程(监视器相当于就是锁的概念)。如果有多个线程在等待只会任意唤醒一个。
public final native void notify();

//唤醒一个在此对象监视器上等待的所有线程(监视器相当于就是锁的概念)。
public final native void notifyAll();

//比较对象内存地址是否相同
public boolean equals(Object o);

//当实例被垃圾回收器回收之后触发,在JDK9之后被废弃。因为其可能会引起死锁以及性能问题
protected void finalize() throws Throwable { }

异常处理

异常机制使得程序能在运行的过程中针对可能出现的错误进行正确处理以保证程序能继续运行下去。在Java中,大致分为两大类:Exception;与Error,二者均继承自Throwable类。在Exception下又被细分为运行时异常和众多的**‘’**。

  • **Throwable:**是Java中所有异常以及错误的父类,只有是他的子类,才可以通过JVM或者throw语句进行抛出处理。其内部包含了一些发生异常时进行查看的方法:
    • getCause(): 返回或者抛出参数异常的原因,如果未知的话,将返回null。
    • getMessage():返回异常消息信息。
    • printStackTrace():打印对象的堆栈跟踪信息。
  • Error: 表示程序运行过程中出现了错误,而此类错误一般来说都是和程序无关的,由于虚拟机执行错误而产生。当错误产生的时候,一般很难通过程序的范畴对其进行解决,因此当出现此类错误的时候,虚拟机一般会选择终止线程而退出。错误包括:内存溢出,虚拟机错误,。
  • Exception: 表示程序允许过程中可能出现的异常,这些异常一般而言是可以在代码层面进行处理的。Exception下具有许多子类:其中可以大致分为运行时异常(RuntimeException)。非运行时异常(IOException,ClassNotFoundException等)。其中非运行时异常是在代码编写的时候必须要对其声明的异常进行处理的,成为必检异常。运行时异常指的是在程序运行过程中由于数据或环境等原因出现的异常,如空指针异常,数据库连接异常等,数组越界异常。这些异常需要在编写代码的时候考虑到并对其进行处理以保证程序正常运行。
  • 异常处理形式: 针对可能出现的异常,在对其进行处理的时候,可以使用以下方式处理:
    • try…catch…finally进行处理,也可以使用throws关键字进行抛出。需要注意的是,如果在执行try…catch…finally代码块的时候,在finally代码块之前遇到了return语句,那么程序仍将先执行完finally代码块的内容再返回。在某些情况下,finally代码块可能并不会执行到:
      • finally代码块发生了错误。
      • 在执行之前使用了System.exit()命令退出。
      • 虚拟机停止。
    • throw关键字,用于方法的内部,抛出一个Throwable类型的异常,在抛出之后,方法的调用者需要对该异常进行处理,如果未处理的话将继续层层上抛至JVM。JVM在收到之后将打印异常消息以及堆栈信息。
    • throws关键字:用于方法体外部的方法声明中,来指明这个方法可能会抛出哪些种类的异常,如果方法声明中指示该方法可能会抛出某个必检异常,那么方法的调用者可以选择使用try…catch进行处理或者继续上抛。
  • **自定义异常:**当在某些情况下想要进行自定义异常时。可以创建类并继承自Exception即可。在需要抛出或处理的地方即可使用该自定义异常。
  • **异常转型(转译):**指的是在发生一种形式的异常时,将该异常以另一种形式的异常抛出处理,下面代码演示了在发生SQLException的时候,会抛出我的一个自定义异常到调用者。:
public void fun(){
	try{
		//doSomething...
	}catch(SQLException e){
		throw new MyException("message");
	}finally{
		//doSomething...
	}
}

接口与抽象类

抽象类抽离了具体一类事物的特征,因此抽象类并非一个具体的事物表述,他需要依靠具体的子类来将其具体化。接口的话,可以看做为一个特殊的“抽象类”,但是二者还是有些不同之处:

  • 从语法层面来说,接口使用interface关键字而抽象类使用abstract关键字修饰一个类。
  • 从功能来说,抽象类更侧重于对一类事物主体特征的抽离,因此可以很明确的由后续的子类确认很多不同的具体实现。接口更适合在分层开发中作为层与层之间连接的桥梁。
  • 接口与抽象类都可以存在变量,但是接口中的变量全部是默认为public static final类型的。而抽象类中的变量则可以按照需求自己定义。
  • 接口中不允许存在构造方法,但是抽象类可以存在。
  • 接口与抽象类都可以定义方法,但是接口中的方法必须全部为方法头而不能有具体的实现逻辑。而抽象类中可以有抽象方法,使用abstract关键字修饰,也可以有已经实现的普通方法。
  • 子类中实现父类中的抽象方法时,可见性可以大于等于父类中;而接口实现类中的接口 方法的可见性只能与接口中相同(public)。
  • 一个子类只能有一个父类(具体类或抽象类),而一个接口可以继承一个多个接口,一个抽象类类也可以实现多个接口。

内部类

内部类可以在一个类中或方法中进行定义,在外部进行引用的时候必须使用以下格式:

内部类名称 对象名称 = new 外部类名称.内部类名称();

当这个类未被static修饰的时候,可以访问他所在的外部类的所有属性以及方法。内部类可以申明为abstract的,并且运行使用访问权限修饰符private和protected。普通内部类内部无法声明静态属性以及方法。

静态内部类

静态内部类是指在一个类的内部再次声明的一个静态的类(使用static关键字修饰)。静态内部类里面可以具有普通成员变量以及静态成员变量 。同时也可以拥有普通方法以及静态方法。但是静态内部类只能访问其外部类的静态属性以及方法。而在外部类的方法中则可以正常使用静态内部类。如果要实例化一个静态内部类,需要按照:

InnerClassName objName = new OutClassName.InnerClassName();

匿名内部类

可以被视为非静态的内部类,因此也具有相同的特点。因为这种类似无名称的,所以是没办法引用的。它在使用的时候需要遵循以下格式:

new <类或接口> <匿名类主体>

匿名类主体可以访问或重写它所依附的类或接口,在Swing编程中经常会用到匿名类来添加监听事件,同样也可以很方便的设置一个线程并运行:

new Thread(){  
  @Override  
  public void run(){  
      System.out.println("Hello!");  
  }  
}.start();

当然有时候可以更加方便的使用Lambda(Java8+)表达式来进行进行书写:

new Thread(()->{  
    System.out.println("Hello!");  
}).start();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值