基础总结
作用域:
成员变量:针对整个类有效。
局部变量:只在某个范围内有效。(一般指的就是方法,语句体内)
存储位置:
成员变量:随着对象的创建而存在,随着对象的消失而消失,存储在堆内存中。
局部变量:在方法被调用,或者语句被执行的时候存在,存储在栈内存中。
匿名对象
(1)匿名对象就是没有名字的对象。是对象的一种简写形式。
(2)应用场景
A:只调用一次类中的方法。
B:可以作为实际参数在方法传递中使用
构造方法:
构造方法没有返回值,方法名和类名一致,参数可有可无,当一个类中不声明构造方法,系统会默认声明一个无参构造方法。如果需要有参构造方法需要自己声明。
如果自定义类有构造方法,那么系统不会给出无参构造方法,这个时候我们可以不是无参构造方法,但如果需要使用无参构造,只能手动声明。
内存结构:
栈内存:用于存储局部变量,当数据使用完,所占空间会自动释放。
堆内存:数组和对象,通过new建立的实例都存放在堆内存中。
方法区:静态成员、构造函数、常量池、线程池
本地方法区:window系统占用
Person p = new Person();在内存中做了哪些事情:
(1)将Person.class文件加载进内存中。
(2)如果p定义在主方法中,那么,就会在栈空间开辟一个变量空间p。
(3)在堆内存给对象分配空间。
(4)对对象中的成员进行默认初始化。
(5)对对象中的成员进行显示初始化。
(6)调用构造代码块对对象进行初始化。(如果没有就不执行)
(7)调用构造方法对对象进行初始化。对象初始化完毕。
(8)将对象的内存地址赋值给p变量,让p变量指向该对象。
static关键字:
(1)静态的意思,用来修饰成员变量和成员函数
(2)静态的特点:
随着类的加载而加载
优先于对象存在
对所有对象共享
可以被类名直接调用
(3)静态的注意事项
A:静态方法只能访问静态成员
为什么:因为静态的内容是随着类的加载而加载,它是先进内存的。
B:静态方法中不能使用this,super关键字
C:主方法是静态的
public static void main(String[] args)
public:公共的意思,是最大权限修饰符。
static:由于jvm调用main方法的时候,没有创建对象。
只能通过类名调用。所以,main必须用static修饰。
void:由于main方法是被jvm调用,不需要返回值。用void修饰。
main:main是主要的意思,所以jvm采用了这个名字。是程序的入口。
String[]:字符串数组
args:数组名
在运行的时候,通过java命令给args数组赋值。
(4)静态变量和成员变量的区别
A:调用方式
静态变量也称为类变量,可以直接通过类名调用。也可以通过对象名调用。
这个变量属于类。
成员变量也称为实例变量,只能通过对象名调用。这个变量属于对象。
B:存储位置
静态变量存储在方法区长中的静态区。
成员变量存储在堆内存。
C:生命周期
静态变量随着类的加载而存在,随着类的消失而消失。生命周期长。
成员变量随着对象的创建而存在,随着对象的消失而消失。
D:与对象的相关性
静态变量是所有对象共享的数据。
成员变量是每个对象所特有的数据。
(5)静态的优点和弊端
优点:
对对象的共享数据进行单独空间的存储,节省内存,没有必要每个对象都存储一份
可直接被类名调用
弊端:
生命周期过长,随着类的消失而消失
访问出现权限,即静态虽好但只能访问静态
(6)什么使用使用静态呢?
A:当所有对象共享某个数据的时候,就把这个成员变量定义为静态修饰的。
B:当某个方法没有访问该类中的非静态成员,就可以把这个方法定义为静态修饰。
静态的生命周期比较长,所以一般不推荐使用。
(7)静态代码块
A:它只执行一次,它比main还先执行。
B:执行顺序
静态代码块–构造代码块–构造方法
继承:
(1)把很多类的相同特征和行为进行抽取,用一个类来描述。让多个类和这个类产生一个关系。
这样的话,多个类就可以省略很多代码。这个关系就是继承。java中用extends关键字表示。
(2)继承的体系结构
A:多个具体的对象,不断的向上抽取共享的内容,最终形成了一个体系。这个体系叫做继承体系。
B:继承体系的学习和使用原则
(3)继承的特点:
A:java中只能单继承,没有多继承。
B:java可以有多重(层)继承。
(4)继承的好处:
继承的出现提高了代码的复用性。
继承的出现让类与类之间产生了关系,提供了多态的前提。
(5)子父类中的成员关系
A:成员变量
在子类方法中使用一个变量时:
首先,在方法的局部变量中找这个变量,有则使用。
否则,在本类中找成员变量,有则使用。
否则,在父类中找成员变量,有则使用。
否则,报错。
B:成员方法
用子类对象使用一个方法时。
首先,在子类中找这个方法,有则使用。
否则,在父类中找这个方法,有则使用。
否则,报错。
final关键字
(1)最终的意思,可以用于修饰类,方法,变量。
(2)final修饰的类不能被继承。
final修饰的方法不能被重写。
final修饰的变量是一个常量。只能被赋值一次。
内部类只能访问被final修饰的局部变量。
抽象:
抽象类和抽象方法都必须由abstract修饰。
抽象类不一定有抽象方法,但有抽象方法的类一定是抽象类。
抽象类不能被实例化。
抽象特点:
A:抽象类中是否有构造方法?能不能被实例化?如果不能,为什么有构造方法?
抽象类中可以有变量和常量。
抽象类中可以有抽象方法和非抽象方法。
抽象类不能实例化,但可以给子类实例化使用。
abstract关键字不能和哪些关键字同事使用:
private私有子类不能继承,final修饰的方法不能被重写。抽象方法必须被重写,static修饰的可以通过类名调用,但抽象方法是没有方法体的。
接口和抽象类的区别
A:抽象类只能被单继承
接口可以多实现,接口的出现避免了多继承的局限性。
B:抽象类中的数据特点:
成员变量:可以是变量,也可以是常量
成员方法:可以是抽象方法,也可以是非抽象方法
构造方法:有构造方法
接口中的数据特点:
成员变量:是常量。默认修饰 public static final
成员方法:都是抽象方法。都有默认修饰 public abstract
构造方法:没有构造方法
try catch:
捕获异常信息,捕获异常原则先捕获小的,在不捕获大的。
finally是异常处理的关键字。必须执行的代码块,当在它执行之前,jvm推出则不执行。
当catch中有return,在return前执行finally代码块。
异常及其处理机制和常见的异常:
异常处理的关键字 throws 和 throw
异常的顶级父类是Throwable。
异常分为两种:一般性错误,在写代码是的错误Exception,RuntimeException运行时异常。
常见的异常:
java.lang.ArithmeticException算数异常。当运行时出现算术的错误(比如,除零)抛出。
java.lang.NullPointerException空指针异常。对null值调用属性和方法时抛出。
Java.long.ArrayIndexOutOfBoundsException 数组下标越界异常。当数组变量引用非法下标时抛出。
Java.long.NuberFormatException 数字格式化异常。当将字符串转成整形时,字符串中含有非数字的字符时抛出。
java.io.FileNotFoundException 文件未找到异常。当对文件进行操作时,错误的书写了文件路径时抛出。
java.sql.SQLException:Column “id2” not found 列名书写错误
java.sql.SQLException:Parameter index cut of range 占位符的数量和参数个数不匹配
com.mysql.jdbc.exceptions.jdbc4.MysqlSyntaxErrorException you have an error in your SQL syntax SQL语句书写错误
java.sql.SQLException: Parameter index out of range SQL语句占位符的数量和参数个数不匹配,检查一下问号的个数和是否是英文问号。
java.sql.SQLException : Column ‘id2’ not found 列书写错误
数据类型和装箱拆箱
八大基本数据类型:
byte 1字节 -128~127
short 2字节 -32768~32767
int 4字节 -2147483648~2147483647
long 8字节 -9223372036854775808 ~ 9223372036854775807
float 4字节 -3.4E38~3.4E38
double 8字节 -1.7E308~1.7E308
char 2字节 从字符型对应的整型数来划分,其表示范围是0~65535
boolean 1字节 true或false
基本数据类型的包装类:
byte → Byte
short → Short
int → Integer
long → Long
float → Float
double → Double
char → Character
boolean→ Boolean
Integer值范围-128~127之间时,返回的为int基本数据类型,当大于127后会new Integer对象。
例:
Douoble是浮点类型,所以数字是无限的,所以Double每次都创建对象。
进行归类:
Integer派别:
Integer、Byte、Short、Long、Character
取值范围:
Double派别:
Double、Float
包装类的equals方法首先将数据装箱,比较的是类型和数值两方面。
集合
ArrayList和LinkedList:
共同点:都是不同步的,线程不安全
不同:
ArrayList的基于动态数组实现,随机访问快,当超过数组长度,会扩充原来的50%,当在集合末尾插入和删除时与LinedList没有差别,当在集合里面插入和删除时效率不如LinkedList。默认ArrayList大小为10,超过则正常原来的3/2;
LinkedList基于链表的数据结构,查询是不能随机方位某个位置的数据,当在集合中添加和删除时,性能优于ArrayList。LinkedList是双向链表,没有初始容量和扩容机制。数据直接追加就行。
Vector:线程同步,当集合不足时,扩充原来的一倍,优于线程是安全的,所以性能较慢。初始容量是10。
哈希
Map:底层实现是数组和链表,使用hashcode和equals方法,保证存储的唯一性。
HashMap: 是非线程安全,key和value允许为null,其中key只允许有一个为null,value可以有多条记录为null;初始容量为16,扩容因子0.75(当前大小/当前容量 = 扩容因子),当达到扩容因子的阈值,则扩展为原来的2倍。
LinkedHashMap:存储是有顺序的。其他与HashMap一致。
HashTable:HashTable是线程安全的,相当于在整个Map加了一个大同步锁来保证线程的安全。key和value都不允许为空,否则会报错,HashTable的Lock锁住了整体。默认容量为1,加载因子0.75。扩容容量 = 原容量 * 2 + 1;
ConcurrentHashMap(jdk7版本):ConcurrentHashMap将Hash表默认分为16个桶(每一个桶可以被看作是一个Hashtable),ConcurrentHashMap的每个Segment可以看做是一个HashTable,所有ConcurrentHashMap的key和value都不可以为空,大部分操作都没有用到锁,而对应的put、remove等操作也只需要锁住当前线程需要用到的桶,而不需要锁住整个数据。采用这种设计方式以后,在大并发的情况下,同时可以有16个线程来访问数据,默认大小为16,阈值0.75。
jdk8版本的ConcurrentHashMap还没看,可以网上自己查找。
Set
基于HashMap实现,相当是HashMap的一个实例。
HashSet允许使用null元素,并保证存入数据不重复。
LinkedHashSet与HashSet一致,但是是有序的。
泛型
jdk1.5以后引入泛型的概念,主要的作用是将运行时遗产提前到编译时。
jdk1.7以后有泛型推断,右面声明不需要写泛型(仅左面写反省起作用)。
泛型类:
public class Generic<T> {
T t;
public void setT(T t) {
this.t = t;
}
public T getT() {
return t;
}
}
泛型方法:
public class Generic {
public <T> void setT(T t) {
// 方法体
}
}
泛型接口:
public interface Genenic<T , E>{
public void demo1(T t);
public void demo2(E e);
}
实现类的几种方式:
//1.都不添加泛型,默认泛型都为Object类型
public class GenenicImpl implement Genenic{
// 实现两个接口
}
//2.添加泛型,泛型为所添加的类型
public class GenenicImpl implement Genenic<String,String>{
// 实现两个接口
}
//3.实现类也不指定泛型
public class GenenicImpl<T , E> implement Genenic<T , E>{
// 实现两个接口
}
反射
反射简单来说就是程序运行时,获取程序一个的字节码文件,来获取这个类的所有属性和方法。反射的技术特性很强大,所以很多优秀的开源框架都用到了。
java反射创建类实例的三种方式:
// 首先创建一个Person类
public class Person{
public String name;
private Integer age;
}
public class Demo {
public static void main(String[] args) {
// 第一种方式
// 由于已拿到对象,所以笔者认为没有必要再使用反射的方式(不推荐)
Person person = new Person();
Class person1 = person.getClass();
System.out.println(person1);
// 第二种方式
// 这种方式虽然简洁,但需要导入依赖包,耦合高(不推荐)
Class person2 = Person.class;
System.out.println(person2);
// 第三种方式
// 这种方式完全解耦(推荐使用)
Class person3 = null;
try {
person3 = Class.forName("Proxy.Person");
System.out.println(person3);
} catch (Exception e) {
}
// 课下小作业:下面都会输出什么结果?为什么?
System.out.println(person1 == person2);
System.out.println(person1 == person3);
System.out.println(person2 == person3);
}
}
其余的反射一般的用法都是通过api那对类中的对象或方法,进行使用,这些可以看java反射的api文档自行学习。
线程
进程:通俗的将就是一个应用程序,有独立的内存空间,一个进程至少有一个线程。
线程:进程的执行单元。
线程的几种状态:
1.New(新建状态)
2.Blocked(阻塞状态)
3.Runnable(运行状态)
4.Time_Waiting(睡眠状态)
5.Waiting(无限等待状态)
6.Terminated(死亡状态)
java中对线程主要的需求是实现多线程,主要有一下几种方式时间多线程:
// 第一种:实现Thread类,重写run方法
public class MyThread extends Thread {
public MyThread() {}
private String name;
// 为了用名称区分线程,加入有参构造
public MyThread(String name) {
this.name = name;
}
@Override
public void run() {
// 实现功能
for (int i = 0; i < 50; i++) {
System.out.println(name + "-->" + i);
}
}
}
// 第二种方式 实现Runnable接口重写run方法
public class RunableDemo implements Runnable {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println(Thread.currentThread().getName() + "-->" + i);
}
}
}
// 第三种方式 实现callable接口,重写call方法
public class CallableDemo implements Callable {
@Override
public Object call() throws Exception {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "-->" + i);
}
return null;
}
}
// 第四种线程池创建多线程可以实现Callable或Runnable
public class ThreadPool implements Runnable {
@Override
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println(Thread.currentThread().getName() + "线程池实现");
}
}
}
// 多线程测试类
public class TestThread {
public static void main(String[] args) {
// 第一种实现方式的测试方法,查看打印结果
MyThread myThread1 = new MyThread("小花");
MyThread myThread2 = new MyThread("小李");
myThread1.start();
myThread2.start();
// 第二种方式测试类
Thread thread3 = new Thread(new RunableDemo());
thread3.setName("小薛");
Thread thread4 = new Thread(new RunableDemo());
thread4.setName("小刘");
thread4.start();
thread3.start();
// 第三种方法的测试类
Callable<Object> oneCallable = new CallableDemo();
Callable<Object> oneCallable1 = new CallableDemo();
FutureTask<Object> oneTask = new FutureTask<>(oneCallable);
FutureTask<Object> oneTask1 = new FutureTask<>(oneCallable1);
Thread t = new Thread(oneTask);
Thread t1 = new Thread(oneTask1);
t.setName("小薛");
t1.setName("小刘");
t.start();
t1.start();
// 第四种方式测试
// 创建线程池 并初始线程数
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 0; i < 3; i++) {
// 将实现类方法
ThreadPool threadPool = new ThreadPool();
// 使用executorService执行Runnable的任务
// 扩展:submit和execute实现的区别什么?
executorService.submit(threadPool);
}
//
executorService.shutdown();
}
}
对于继承Thread类和实现Runnable接口两种方式来说,通过实现接口的形式更加灵活:
①继承只能是单继承,实现则更加灵活。
②接口方式将run方法的方法体(线程任务)和开启线程的strat方法(开启线程)进行分离,使其接口,方便实现先成任务的切换。
Runnable和Callable区别:
①Runnable要实现run方法,Callable实现call方法。
②Runnable无返回值,Callable能返回执行结果。
③call方法可以抛出异常,run方法只能内部自行解决。
说到多线程,就不得不提到线程安全问题。最经典的线程安全问题就是抢票。
// 多线程抢票:
public class MyThread {
public static void main(String[] args) {
BuyTicket buyTicket= new BuyTicket();
Thread t1 = new Thread(buyTicket);
Thread t2 = new Thread(buyTicket);
Thread t3 = new Thread(buyTicket);
t1.start();
t2.start();
t3.start();
}
}
// 使用Runnable,重写run方法,构建枪票代码
class BuyTicket implements Runnable {
private int ticket = 100;
@Override
public void run() {
while (true) {
if (ticket > 0) {
// 为了提高异常发生概率
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "出售第" + ticket + "票");
ticket--;
}
}
}
}
输出结果:
保证线程安全的几种方式:
// 同步代码块
public class MyThread {
public static void main(String[] args) {
BuyTicket buyTicket = new BuyTicket();
Thread t1 = new Thread(buyTicket);
Thread t2 = new Thread(buyTicket);
Thread t3 = new Thread(buyTicket);
t1.start();
t2.start();
t3.start();
}
}
class BuyTicket implements Runnable {
private int ticket = 100;
// 同步锁 任意对象
Object obj = new Object();
@Override
public void run() {
while (true) {
// synchronized锁的关键字 同步代码块
synchronized (obj) {
if (ticket > 0) {
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "出售第" + ticket + "票");
ticket--;
}
}
}
}
}
// 同步方法
public class MyThread {
public static void main(String[] args) {
BuyTicket buyTicket = new BuyTicket();
// 验证锁对象是不是同一个BuyTicket对象
// System.out.println("run :" + buyTicket);
Thread t1 = new Thread(buyTicket);
Thread t2 = new Thread(buyTicket);
Thread t3 = new Thread(buyTicket);
t1.start();
t2.start();
t3.start();
}
}
class BuyTicket implements Runnable {
private int ticket = 100;
@Override
public void run() {
// 验证锁对象是不是同一个BuyTicket对象
// System.out.println("this" + this);
while (true) {
// 同步方法
safe();
}
}
// 同步方法的锁对象就是 BuyTicket
public synchronized void safe() {
if (ticket > 0) {
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "出售第" + ticket + "票");
ticket--;
}
}
}
// 静态同步方法
public class MyThread {
public static void main(String[] args) {
BuyTicket buyTicket = new BuyTicket();
Thread t1 = new Thread(buyTicket);
Thread t2 = new Thread(buyTicket);
Thread t3 = new Thread(buyTicket);
t1.start();
t2.start();
t3.start();
}
}
class BuyTicket implements Runnable {
private static int ticket = 100;
@Override
public void run() {
while (true) {
// 同步方法
safe();
}
}
// 静态同步方法的锁对象就是本类的class对象 就是BuyTicket.class
public static synchronized void safe() {
if (ticket > 0) {
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "出售第" + ticket + "票");
ticket--;
}
}
}
// 使用Lock锁
public class MyThread {
public static void main(String[] args) {
BuyTicket buyTicket = new BuyTicket();
Thread t1 = new Thread(buyTicket);
Thread t2 = new Thread(buyTicket);
Thread t3 = new Thread(buyTicket);
t1.start();
t2.start();
t3.start();
}
}
class BuyTicket implements Runnable {
private static int ticket = 100;
//
Lock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
lock.lock();
if (ticket > 0) {
try {
Thread.sleep(20);
System.out.println(Thread.currentThread().getName() + "出售第" + ticket + "票");
ticket--;
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}
}
Lock和synchronized优缺点:
synchronized是java语音自带的关键字,所以有内置特性,不需要手动去释放锁。
synchronized释放锁:
①占有锁的线程任务执行完。
②占有锁线程发生异常,jvm会自动释放锁。
③占有锁线程进入WAITING状态,调用wait()方法。
当使用synchronized关键字,占有锁的线程由于io等原因倒是线程阻塞(相当于sleep),那么会发生死锁,而lock可以设置超时时间。避免发生死锁现象。
多线程的情况下,读写文件时,读和写、写和写会发生冲突,但读和读是可以同时进行的,但使用synchronizedze只能有一个线程执行读操作。Lock有多线程的读操作机制。
Lock的tryLock()方法可以判断是否成功获取了锁,这个是synchronized无法办到的。
IO流
由于IO流总结起来太费时间,而且常用的也不是很多,所以借鉴一下大神的笔记。
链接: 应该是大神的笔记,学习一下。
里面很详细的介绍了IO流。
java基础篇我个人感觉重要的算是总结完毕。
纯属自己总结,有错误的希望大家指出,不喜勿喷。谢谢了!~!