Java基础语法
一、数据类型
- 分类
(1)基本数据类型:byte,short,int,long,float,double,char,boolean
(2)引用数据类型:类(class)、接口(interface)、数组([])
二、访问修饰符号
定义:Java中,使用修饰符来保护对类、变量、方法和构造方法的访问
- Private:同一类内可见。使用对象:变量、方法(注:不能修饰外部类)
- Default:在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。常用于接口中修饰某方法,即表明次方法不需要实现
- Protect:对同一包类和所有子类可见。使用对象:变量、方法(注:不能修饰外部类)
-
Public:对所有类可见。使用对象:类、接口、变量、方法
三、&和&&的区别
- &:位运算符(不具有短路特点,左右都要运算)
- &&:逻辑运算符(具有短路特点)
四、Final(用于修饰类、属性和方法)
- 被final修饰的类不可以继承
- 被final修饰的方法不可被重写
- 被final修饰的变量不可以被改变,即改变量的值在初始化后不能被改变(如果修饰引用,那么表示引用不可变,引用指向的内容可变)
- 被final修饰的方法,JVM会尝试将其内联,以提高运行效率
- 被final修饰的常量,在编译阶段会存入常量池中
五、Final、finally、finalize区别
- final可以修饰类、变量、方法,类不能继承,方法不能重写,变量不能被重新赋值
- Finally是一个关键之,一般作用与try-catch代码块中,在处理异常时,通常将一定要执行的代码方法放到finally{}代码块中,表示是无论是否出现异常,该代码块都会执行,一般用来存放关闭资源的代码
- Finalize是一个方法,属于Object的一个方法,Object类是多有类的父类,该方法一般由垃圾回收器来调用,当我们调用System.gc() 方法的时候,由垃圾回收器调用finalize(),回收垃圾,一个对象是否可回收的最后判断。
六、This关键字的用法
This是自身的一个对象,代表对象本身,理解为:指向对象本身的一个指针
用法:
- 代表自身对象
class SelfObject {
// SelfObject object;
public SelfObject() {
//object = this;
public void test() {
System.out.println(this)
}
}
}
- 引用成员变量(区分函数参数和成员变量同名)
public Person(String name, int age) {
this.name = name;
this.age = age;
} - 引用构造方法(只能引用一个构造方法,且在第一行)
class Constructor {
int a ;
public Constructor() {
this(0);
}
public Constructor(int a) {
this.a = a;
}
}
- 引用成员方法(this不能用在static方法中)
this.方法名(参数)
七、Super关键字的用法
理解为:指向自己超(父)类对象的一个指针,这个超类指的是离自己最近的一个父亲
- 普通的直接引用
可以用 super.xxx 来引用父类的成员 - 子类中的成员变量或方法与父类中的成员变量或方法同名
- 引用构造函数
八、static的用法
被static关键字修饰的方法或者变量不需要依赖于对象来进行访问,只要类被加载了,就可以通过类名去进行访问。
用途:static可以用来修饰类的成员方法、类的成员变量,另外可以编写static代码块来优化程序性能。
static方法:
没有this,因为不依附于任何对象。静态方法不能访问类的非静态变量和非静态成员方法,因为非静态成员方法和非成员变量都必须依赖具体的对象才能被调用。非静态方法可以访问静态方法和静态变量
最常见的static方法就是main方法,至于为什么main方法必须是static的,现在就很清楚了。因为程序在执行main方法的时候没有创建任何对象,因此只有通过类名来访问-
static变量:
- 静态变量:被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化(初始化顺序按照定义的顺序进行初始化)
- 非静态变量:对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响
static代码块:
static块可以置于类中的任何地方,类中可以有多个static块。在类初次被加载的时候,会按照static块的顺序来执行每个static块,并且只会执行一次。
为什么说static块可以用来优化程序性能?
因为它的特性:只会在类加载的时候执行一次,不会产生资源的浪费。因此将一些只需要进行一次的初始化操作都放在static代码块中进行
九、流程控制语句
- break:跳出最上一层循环,不再执行循环
- continue:跳出本次循环,继续执行下次循环
- return:程序返回,不再执行下面的代码(接触当前方法,直接循环)
九、Object的九大方法
equals() 判断两个对象是否相等
clone() protected方法,浅拷贝 clone两次时深拷贝
浅拷贝与深拷贝的区别:
浅拷贝:拷贝对象与实际对象指向同一个地址空间,当拷贝对象的内容被修改的时候,实际对象的内容也会被修改,他们共同维护一个副本。
深拷贝:拷贝对象与实际对象指向不同的地址空间,当拷贝对象的内容被修改的时候,实际对象并不会受影响,他们是两个不同的对象,只不过内容相同。
getClass() final方法,不可重写,获取对象运行时的class
notify() 唤醒该对象上等待某个线程,如果有多个线程在对象上等待,那么按照一定算法唤醒其中一个
notifyAll() 唤醒在该对象上等待的所有线程
wait() 使当前线程在该对象上等待
toString() 转换为字符串
hashCode() 获取对象的哈希值,一般情况下根据对象的地址或者数字计算
finalize() protected方法,当对象被释放时,该方法会被调用,进而程序员可通过此方法释放JVM无法管理的内存,以免内存泄露
十、equals()、“==”、hasCode()
- ==: 运算符,用于比较两个变量是否相等
- 基本数据类型:比较值
- 引用数据类型:比较内存的值
- equals()
- object比较两个对象指向的地址是否相同
- Integer,String等进行重写,比较两个对象的内容是否相同
String a = new String("abcd");
String b = new String("abcd");
String c = "abcd";
String d = "abcd";
//false,非同一对象
if (a == b) {
print....
}
//true,在String中equals被重写,当物理地址不同时,会进一步比较值
if(a.equals(b)){
print....
}
//true
if (c == d) {
print...
}
//true
if (c.euqals(d)) {
print....
}
- hasCode():用于计算对象的hash值,返回一个int值,确定该对象在哈希表中的索引位置,实现快速查找,但会产生hash冲突
解决hash冲突方法:链地址法、限性探测法、二次hash法
hasCode()和equals()的关系
- 重写equals()必须重写hasCode()
- 哈希春初结构中,添加元素重复性校验时,先检查hashCode值,后判断equals()
- 两个对象equals()相等,hasCode()必相等
- 两个对象hasCode()不相等,equals()必定不相等
- 两个对象hasCode()相等,对象不一定相等,需要通过equals()进一步判断
十一、重载与重写
重载:不同函数使用相同的函数名,函数参数列表不同,参数个数或者参数类型不同,或者返回类型不同
重写:使用同一个函数,对方法体进行重写,用于满足需求,例如继承时子类重写父类的方法
十二、String、StringBuilder、StringBuffer
共同点:都是final类,都不能被继承
-
区别:
- String:字符串常量,不可变,底层char数组使用final修饰,每次调用api产生信对象
---private static final char[];
每次对String类型进行操作都等同于产生了一个新的String对象,然后指向新的String,所以尽量不要对String进行大量的拼接,否则会产生大量临时对象,导致GC(垃圾回收)开始工作,影响性能
- StringBuffer和StringBuilder:可变,可动态扩展
StringBuffer(线程安全):对象本身操作,不产生新的对象,底层方法都用Synchronized修饰,适合多线程环境下使用
StringBuilder(线程不安全):适合单线程使用,速度快
- String:字符串常量,不可变,底层char数组使用final修饰,每次调用api产生信对象
十三、序列化与反序化
序列化:把Java对象转换为字节序列的过程
-
反序列化:把字节序列回复为Java对象的过程
场景:
- 实现数据持久化(数据库/文件):通过序列化可以把数据永久的保存在硬盘上
- 实现远程数据传输:即在网络上传递对象的字节序列
作用:保证数据一致性,即保证输入输出一致
java实现:- 实现Serializable接口
- 将对象转换为输出流,读取输出流
反序列化转换为输入流,读取输入流
数据格式:xml,json
十四、java异常
java异常主要分为Error和Exception,他们都继承自Throwable
Error
主要为系统内部错误,是没有办法进行捕获的,例如jvm栈崩溃,内存溢出,用户是没办法进行处理的,只能强制结束程序。-
Exception
异常,主要由程序代码引起的,是可以进行捕捉捕获解决的
Eg:
NullPointerException(空指针异常)
ArrayIndexOutOfBoundsException(数组下标越界)- Exception分为checkException和UnCheckException
checkException:受检查异常,需要进行try catch,否则无法通过编译
Eg:
sqlException、IOException
UnCheckException:RuntimeException,可以通过编译,运行时抛出异常
- Exception分为checkException和UnCheckException
十五、反射
原理:java类文件编译完成后生成字节码.class文件,反射就是根据类的全限定名找到对应的字节码文件,获取对应的实例对象,以及方法即属性。动态获取对象。(在正常使用一个类时,我们需要提前了解这个类有什么方法,有哪些属性。而利用反射我们在运行时才了解该类有什么方法和属性。)
运用:jdbc连接,jdk动态代理
优点:动态获取,方便灵活
缺点:无法对代码进行优化修改,安全性(可以修改访问权限)
Class类的对象作用:运行时提供或获得某个对象的类型信息
获取Class对象方法:
- Class.forName():将类进行装载并做类的静态初始化,返回Class对象.
- Class.class:jvm将类装入内存,不做类的初始化工作,返回Class对象
- getClass():对类进行静态初始化、非静态初始化,返回引用运行时真正所指的对象(向下转型)
创建对象:new Instance();
获取方法:getMethods()(public),getDeclaredMethods(private)
获取属性:getFields(public),getDeclaredFields(private)
- 通过反射获取类对象的三种方法
public void TestTwo() throws Exception{
//通过persion实例获取
Person preson1 = new Person();
Class c1 = preson1.getClass();
System.out.println(c1.getName());
//通过presion类直接获取,不会执行静态代码块(static代码块)
Class c2 = Person.class;
System.out.println(c2);
//通过Class的静态方法获取
String classname = "com.company.Person";
Class c3 = Class.forName(classname);
System.out.println(c3);
}
十六、内部类
概念:内部类是指一个在一个外部类的内部再定义一个类,类名不需要和文件夹相同。
- 使用内部类的原因
每个内部类都能独立地继承自一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。如果没有内部类提供的可以继承多个具体的或抽象的类的能力,一些设计与编程问题就很难解决。从这个角度看,内部类使得多重继承的解决方案变得完整。接口解决了部分问题,而内部类有效地实现了“多重继承”。
- 成员内部类
作为外部类的成员,可以直接使用外部类的所有成员和方法,即使是private的,外部类要访问内部类的成员和方法,需要通过内部类的对象来获取。成员内部类不能拥有static方法和属性,因为它需要先创建外部类才能创建自己的。
public class OuterClass {
private String str;
public void outerDisplay(){
System.out.println("outerClass...");
}
public class InnerClass{
public void innerDisplay(){
//使用外围内的属性
str = "chenssy...";
System.out.println(str);
//使用外围内的方法
outerDisplay();
}
}
/*推荐使用getxxx()来获取成员内部类,尤其是该内部类的构造函数无参数时 */
public InnerClass getInnerClass(){
return new InnerClass();
}
public static void main(String[] args) {
OuterClass outer = new OuterClass();
OuterClass.InnerClass inner = outer.getInnerClass();
inner.innerDisplay();
}
}
- 局部内部类
局部内部类是定义在一个方法或者一个作用域里面的类,它的访问权限仅于方法或者作用域内。
public class Parcel6 {
private void internalTracking(boolean b){
if(b){
class TrackingSlip{
private String id;
TrackingSlip(String s) {
id = s;
}
String getSlip(){
return id;
}
}
TrackingSlip ts = new TrackingSlip("chenssy");
String string = ts.getSlip();
}
}
public void track(){
internalTracking(true);
}
public static void main(String[] args) {
Parcel6 parcel6 = new Parcel6();
parcel6.track();
}
}
- 静态内部类
静态内部类也是定义在一个外部类里面的类,只是在类前面多了static关键字。静态内部类不需要依赖于外部类,和静态成员属性有点相似,不能使用外部类的非static方法和属性。
public class OuterClass {
private String sex;
public static String name = "chenssy";
/**
*静态内部类
*/
static class InnerClass1{
/* 在静态内部类中可以存在静态成员 */
public static String _name1 = "chenssy_static";
public void display(){
/*
* 静态内部类只能访问外围类的静态成员变量和方法
* 不能访问外围类的非静态成员变量和方法
*/
System.out.println("OutClass name :" + name);
}
}
/**
* 非静态内部类
*/
class InnerClass2{
/* 非静态内部类中不能存在静态成员 */
public String _name2 = "chenssy_inner";
/* 非静态内部类中可以调用外围类的任何成员,不管是静态的还是非静态的 */
public void display(){
System.out.println("OuterClass name:" + name);
}
}
/**
* @desc 外围类方法
*/
public void display(){
/* 外围类访问静态内部类:内部类. */
System.out.println(InnerClass1._name1);
/* 静态内部类 可以直接创建实例不需要依赖于外围类 */
new InnerClass1().display();
/* 非静态内部的创建需要依赖于外围类 */
OuterClass.InnerClass2 inner2 = new OuterClass().new InnerClass2();
/* 方位非静态内部类的成员需要使用非静态内部类的实例 */
System.out.println(inner2._name2);
inner2.display();
}
- 匿名内部类
没有名字的内部类,只能使用一次,用来简化代码,通常用于继承父类和实现接口。例如多线程继承Thread和实现Runnable接口。
public class Demo {
public static void main(String[] args) {
Thread t = new Thread() {
public void run() {
for (int i = 1; i <= 5; i++) {
System.out.print(i + " ");
}
}
};
t.start();
}
}
- 内部类的作用
解决单继承的弊端,实现多继承
内部类可以很好的隐藏实现
可以避免父类和接口同方法名时的覆盖问题
十七、抽象类与接口
抽象类:使用abstract关键字进行修饰,是为了继承而使用,用于描述子类的公共特性</br>
接口:使用interface关键字进行修饰,是对行为的抽象
区别:
- 方法上:抽象类除了抽象方法外还可以拥有普通方法以及普通方法的具体实现,而接口只能拥有抽象方法
- 成员变量上:抽象类除了拥有静态常量外还可以拥有普通成员变量,而接口只能有静态常量
- 代码块上:抽象类可以拥有静态方法或者静态代码块,而接口是不允许的
- 使用上:一个类只能继承一个抽象类,但可以实现多个接口
- 抽象类有构造器但不能被实例化,接口没有构造器
十八、泛型
概念:泛型的本质是参数化类型,将操作的数据类型转化为方法的参数,这种参数类型可以用在类,接口和方法的创建中,构成泛型类,泛型接口与泛型方法。泛型可以针对泛化的数据类型编写相同的算法。
特点:
- 发逆行就是编写模板代码适应任意类型
- 不必对类型进行强制转化
- 编译器将对类型进行检查
- 注意泛型的继承
- 可以把ArrayList<Integer>向上转型为List<Integer>(T不能变)
- 不可以把ArrayList<Integer>向上转型为ArrayList<NUmber>
使用:
- 需要把泛型参数<T>替换为需要的class类型
List<String> list = new ArrayList<String>(); - 可以胜率编译器自动推断类型
- 不指定泛型类型时,编译器会给出警告,且只能将<T>视为Object类型
java:采用的擦除式泛型,即泛型只存在于源代码中,编译完成之后,泛型全部指向裸类型(完成编译后的Class文件中,泛型信息却不复存在了,JVM在运行期间对泛型无感知)
比如:List<Integer>,List<String>编译完成之后都指向List,当进行数据插入或者更改的时候,再根据数据类型进行强制转换,如果是基本类型,直接进行自动装箱拆箱。
- 泛型接口(待排序的元素)
public interface Comparable<T> {
/**
* 返回-1:当前实例比参数o小
* 返回0:当前实例与参数o相等
* 返回1:当前实例比参数o大
*/
int compareTo(T o);
}
class Person implements Comparable<Person> {
private String name;
private int Score;
public int comparable(Peraon o) {
if (this.score < o.score) {
returen -1;
} else if (this.score > o.score) {
returen 1;
} else {
return this.name.comparable(o.name)
}
}
}
- 泛型类
//定义
class Point<T>{// 此处可以随便写标识符号
private T x ;
private T y ;
public void setX(T x){//作为参数
this.x = x ;
}
public void setY(T y){
this.y = y ;
}
public T getX(){//作为返回值
return this.x ;
}
public T getY(){
return this.y ;
}
};
//IntegerPoint使用
Point<Integer> p = new Point<Integer>() ;
p.setX(new Integer(100)) ;
System.out.println(p.getX());
//FloatPoint使用
Point<Float> p = new Point<Float>() ;
p.setX(new Float(100.12f)) ;
System.out.println(p.getX());
- 泛型方法
十九、BIO,NIO
BIO:BIO是传统的javaIO包,基于流模型去实现的,是同步阻塞的,在读入输入流或者输出流时,在读写动作完成之前,线程会一直阻塞在那里。优点是代码简单直观,缺点是IO的效率和扩展性低。
Nio:java引入的IO包,提供了Selector,Channel,Buffer等抽象,可以构建多路复用,同步非阻塞IO
- Buffer:BIO面向流,而Nio面向缓冲区,Buffer是一个对象,包含输入输出的数据,NIO所有数据都是用缓冲区处理的,读数据时直接读到缓冲区,写数据时写到缓冲区。
- Channel:NIO通过Channel进行读写,Channel是双向的,可读可写,通过Channel与Buffer进行交互。
- selector:选择器用于使用单线程处理多个通道,一个Select可以处理多个Channel,提升效率,实现复用。