一、概述
JVM、JRE、JDK的区别
- JVM:Java虚拟机
- JRE:Java虚拟机 + Java程序核心类库(lang包等)
- JDK:JRE + Java开发工具(编译工具,打包工具等)
跨平台原理
JAVA程序是通过Java虚拟机在系统平台上运行的,Java源代码经过虚拟机编译后成字节码文件(.class),他不面向任何特定的处理器,只面向虚拟机
Java程序的主类
一个程序可以很多类,但是只能有一个主类,在Java应用程序中,这个主类是指包含main方法的类,而在小程序中,这个主类是一个继承自japplet或Applet的子类,应用程序的主类不一定要求是public类
Java应用程序与小程序的区别
Java应用程序通过主线程启动,applet小程序没有main方法,主要是嵌在浏览器页面行运行,通过init线程或run来启动
二、基础概念
数据类型
Java是强类型语言,对于每一种数据都定义了明确的具体的数据类型,在内存中分配了大小不同的空间
分类
- 基本数据类型
整型:byte(1字节)、short(2)、int(4)、long(8)
浮点型:float(4)、double(8)
字符型:char(2)
布尔型:boolean(1)
1字节 = 8位,在计算机系统中,一个0或者1占一位的存储空间
- 引用数据类型
数组、类、接口
其他
- long不可以作用在Switch上
Java12中引入了对long的支持,也就是说,Java12以后就可以了
- 最效率的计算方法:位运算,2<<3; 左移3位相当于乘以2的三次方,右移3位相当于除以2的三次方
- 高精度赋值给低精度,属于窄化,会造成精度损失
- += 进行了隐式地强制类型转换;short i += 1 -> i = (short)i + 1;
访问修饰符
- public:公共,对所有包所有类可见,可用于:类、接口、属性、方法
- protected:受保护的,对同一包内的类及子类可见,可用于:属性、方法,不能修饰类
- default:缺省,同一包内可见,但不包括子类,可用于:类、接口、属性、方法
- private:私有,同一类可见,不包括子类,可用于:方法、属性,不能修饰类
运算符
- &与&&,|与||:&是逻辑与,要求运算符两边都是TRUE,结果才位TRUE;&&是短路与,同样要求运算符两边都是TRUE,但是,如果运算符的左边为FALSE,那么表达式就会被直接短路,不会在继续计算右面的表达式,|与||同理
关键字
- final:用于修饰类和方法及属性;修饰类:此类不可继承、修饰方法:此方法不可重写、修饰属性:此属性不可再被赋值(不可再改变属性的引用,而不是引用指向的内容,引用指向的内容可以改变)
- finally:通常用在try - catch 代码块中,表示不管是否出现异常,这个代码块都会被执行,通常用于关闭某些需要手动关闭的资源
- finalize:这是一个方法,属于object类,一般由垃圾回收器来调用,当我们调用system . gc ( ) 方法时,由垃圾回收器调用finalize回收垃圾,一个对象是否可回收的最后判断yan
流程控制语句
- break:跳出循环
- continue:跳出本次循环
- return:结束方法
- 跳出多重循环:在循环体外加入标号(标号 :),使用break 标号;即可跳出外层循环
三、面向对象
三大特性
- 封装
- 继承:子类拥有父类的非private属性和方法
- 多态:程序中定义的引用变量所指向的具体类型(可能是父类子类)和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量到底会指向哪个类的实例,该引用变量发出的方法调用哪个类中实现的方法,必须在程序运行期间才能决定;实现多态的两种方式:继承和接口
类与接口
抽象类与接口
- 抽象类用于捕捉子类的通用特性,而接口用于规范实现类的行为
共同点:
- 接口和抽象类都不能实例化
- 都位于继承的顶端,用于被其他类继承或者实现
- 都包含抽象方法,其子类需要重写这些方法
抽象类的子类如果没有实现这些抽象方法,那么它本身也必须声明为一个抽象类,否则就必须实现父类抽象类所有的抽象方法
在Java8中,接口引入了默认方法,可以在接口中提供具体的实现,而子类不需要实现这些方法,让接口更加灵活,这是接口演化的结果,在这之前,一个接口被定义好以后,如果要添加新的方法,那么它所有的实现类都需要也增加对应的方法,引入默认方法后,实现类就可以有选择地实现这些方法
区别:
- 其实现的关键字不同:继承:extends,实现:implement
- 其声明的关键字不同:抽象类:abstract,接口:interface
- 构造器:抽象类可以有构造器,接口没有
抽象类中构造方法有两个作用:
- 初始化抽象类的成员变量
- 实现类在实例化的时候需要调用父类的构造方法进行初始化工作,因为子类继承了父类的属性和方法,需要确保父类的成员变量得到了正确的初始化
- 一个类如果没有显式地声明构造方法,那么Java会提供一个无参的构造方法,当子类初始化的时候,如果没有显式地调用父类的构造方法,那么就会隐式地调用父类的无参构造方法,但是如果父类只有有参构造方法,那么子类在初始化的时候,必须显式地调用父类的有参构造方法,并传入对应的参数,否则会编译报错
- 访问修饰符:抽象类中的方法:随意,接口中的方法:不允许private或者protected
- 一个类只能继承一个抽象类,但是可以实现多个接口
- 抽象类的属性可以用任意的修饰符,但是接口的属性都是public、static和final的,也就是静态常量
普通类与抽象类的区别
- 普通类不能包含抽象方法
- 抽象类不能直接实例化
- 抽象类不能用final来修饰,因为它声明出来就是用来给其他类继承的
变量与方法
1. 成员变量与局部变量
- 成员变量:定义在方法外,作用域是整个类,随对象的创建与销毁而创建于销毁,存储在堆内存,默认是有初始值的
- 局部变量:定义在方法内,作用域只在方法,随语句执行的结束而结束,存储在栈内存,默认无初始值,使用前需赋值
2. 无参构造的作用
- Java程序在执行子类的构造方法前,如果没有通过super()来调用父类特定的构造方法,那么就会默认调用父类中的无参构造。
3. 构造函数
-
作用:帮助完成对类对象的初始化工作,一个类没有显式声明构造函数的时候,也会有默认的无参构造
-
特征:
- 方法名与类名相同
- 无返回值,但是不能使用void声明
- 生成类的对象时自动执行,无需调用
4. 静态变量与实例变量的区别
- 静态变量:不属于任何对象,属于类,所以在内存中只有一份,在类的加载过程中,JVM只为静态变量分配一次内存空间
- 实例变量:每次创建对象,都会给每个对象分配成员变量的内存,实例变量属于实例对象,在内存中,创建多少个对象,就有几份成员变量
内部类
- 内部类可以分为四种:
- 成员内部类:定义在类内部的非静态类;创建方式:外部类实例 . new 内部类()
- 局部内部类:定义在方法内的非静态内部类;创建方式:new 内部类()
- 局部内部类:定义在方法内的非静态内部类;创建方式:new 内部类()
- 静态内部类:定义在类内部的静态类;创建方式:new 外部类 . 静态内部类()
重载与重写
- 重载:同一类内,声明同名但是不同参数的方法(参数个数不同,类型或顺序不同),与方法的返回值和修饰符无关
这里的顺序不同,是在类型不同的前提下,比如说:
对于方法:add (int a, int b)
这样不属于重载:add (int b, int a)
在类型不同的时候,对于方法:add (int a, double b)
这样才属于重载:add (double b, int a)
- 重写:父子类之间,方法名与参数相同
重写时的访问修饰符和异常:
子类的访问修饰符范围不能比父类的访问修饰符小
子类抛出的异常范围不能比父类抛出的异常大
对象相等的判断
- == :比较基本数据类型:比较值;比较引用类型数据:比较内存地址
- equals:如果没有重写equals方法,用于比较对象的时候,等价与 == ;如果重写了equals,比较对象的时候,比较的是对象值(比较内容)
为什么重写了equals必须重写hashcode
- hashCode返回的哈希码用于在快速定位对象在哈希表等数据结构中的位置
- 重写了equals以后,两个具有相同内容的对象比较返回的值是true,也就是可以认定它们是同一个对象
- 如果没有重写hashCode,那它们返回的哈希码就不痛,就不在同一个位置,这违反了一致性原则
值传递
当一个对象被当做参数传递到一个方法后,可以在方法内修改这个对象的属性,那么这里是值传递还是引用传递?
值传递,Java语言的方法调用只支持值传递,方法只是得到了参数对象的拷贝,修改对象的拷贝并不会影响到对象本身
Java包
- Java.lang
- java.io
- java.nio
- java.net
- java.net
- java.net
io流
io流分几种
- 根据流向:输入流、输出流
- 根据操作单元:字节流、字符流
- 根据角色划分:节点流、处理流
inputStream/Reader是所有输入流的基类,前者是字节输入流,后者是字符输入流
outputStream/writer是所有输出流的基类,前者是字节输出流,后者是字符输出流
BIO、NIO、AIO
- BIO:block io,同步阻塞式io,并发处理能力低
- NIO:non io,同步非阻塞式io,传统io的升级
- AIO:异步非阻塞式io,NIO的升级
files的常用方法
- files.exists:检测文件路径是否存在
- fies.createFile:创建文件
- files.createDirectory:创建文件夹
- files.delete:删除文件
- files.copy:复制文件
反射
- 反射机制是指在运行状态中,对于任意一个类,都能知道这个类的所有方法和属性,对于任意一个对象,都能调用它的任意属性和方法,这种动态获取信息以及动态调用对象的方法的功能称为Java的反射机制
常用API
string
-
字符型常量与字符串常量的区别:char与string
-
字符串常量池
位于堆内存中,专门用来存储字符串常量,可以提高内存的使用率,避免开辟多块空间存储相同的字符串,在创建字符串的时候,JVM会首先检查常量池,如果已经存在就返回它的引用,如果不存在就实例化一个字符串放到常量池中,并返回其引用 -
string的特性
- 不变性:string是只读字符串(final修饰的类,可以通过反射修改),对它进行的任何操作都是创建一个新的字符串,再把引用指向新字符串,这个特性的主要作用是,多线程操作时,可以保证数据的一致性
- 常量池优化
- 它是一个final修饰的char类型数组
- 常用方法
- indexOf:返回指定字符的索引
- charAt:返回指定索引的字符
- replace:字符串替换
- trim:去除字符串两边的空白
- split:分割字符串,返回一个分割后的字符串数组
- getBytes:返回字符串的bytes类型数组
- getBytes:返回字符串的bytes类型数组
- substring:截取字符串
- equals:比较字符串
- 使用hashmap的时候,用string做key的好处
hashMap需要通过key的hashcode来计算存储位置,因此字符串是不可变的,所以当创建了字符串以后,它的hashcode被缓存下来了,不需要经过再次计算
- string、stringBuffer、stringBuilder
- stringBuffer与stringBuilder都继承自AbstractStringBuilder(定义了一些基本的字符串操作),它们都是可变的,而string不可变
- stringBuffer与stringBuilder都继承自AbstractStringBuilder(定义了一些基本的字符串操作),它们都是可变的,而string不可变
- string每次操作都会生成一个新的对象,而stringBuffer是对对象本身进行操作,所以性能更高
- 需要操作少量数据时使用string、需要单线程操作大量数据时使用stringBuilder、需要多线程操作大量数据时使用stringBuffer
包装类
- 自动拆箱与自动装箱
- 装箱:将基本数据类型用它们对应的引用类型包装起来
- 拆箱:将包装类转换为基本数据类型
- int 和 integer 的区别
- 引入包装类的原因:为了能将这些基本数据类型当成对象操作
- int 存储在常量池,integer存储在堆空间
整型字面量在-128 到 127之间,那么自动装箱时不会new新的integer对象,而是直接引用常量池的integer对象