1、JAVA语法基础
标识符 关键字 数据类型与类型转换
-
标识符 :字母、数字、下划线(_)、美元符($),不能包含别的特殊符号。 不能以数字开头,标识符不能是java关键字。
-
关键字:有53个关键字。其中有两个保留字:const和goto
-
数据类型:
Integer 在-128到127里直接在常量池 。
问题:基本类和包装类区别?
1、包装类是对象,拥有方法和字段,对象的调用都是通过引用对象的地址,基本类型不是
2.声明方式不同,基本数据类型不需要new关键字,而包装类型需要new在堆内存中进行new来分配内存空间
3.存储位置不同,基本数据类型直接将值保存在值栈中,而包装类型是把对象放在堆中,然后通过对象的引用来调用他们
4.初始值不同,int的初始值为 0 、 boolean的初始值为false 而包装类型的初始值为null
5、使用方式不同,基本数据类型直接赋值使用就好 ,而包装类型是在集合如 coolection Map时会使用
问题:char能存汉字吗? char类型占两个字节,而java默认采用Unicode码是16位,所以一个Unicode码占两个字节,java中无论汉字还是英文都是用Unicode编码来表达的,所以可以存储一个汉字。
4.ASCII码表:编码65是【A】,编码97是【a】。
5.逻辑运算符:&&是短路与,1&&2,当,1为false,2会被短路,提高程序运行效率。
6.break与count:break直接跳出当前循环的循环体,continue跳出本轮循环,继续下 一轮循环。
7.创建数组过程分析:程序创建数组 int[] a = new int[5]; 时发生了什么?
-
在内存中开辟连续的空间,用来存放数据,长度是5
-
给数组完成初始化过程,给每个元素赋予默认值,int类型默认值是0
-
数组完成初始化会分配一个唯一的地址值
-
把唯一的地址值交给引用类型的变量a去保存
8.创建对象过程分析: Person p = new Person();
9.Arrays.toString(数组)与Arrays.sort(数组)
Arrays.tostring: 把数组里的数据,用逗号连接成一个字符串[值1,值2]。
Array.sort: 对数组进行排序,对于基本类型的数组使用的是优化后的快速排序算法,效 率高 对引用类型数组,使用的是优化后的合并排序算法
10、重载与重写:
重载:(两同一不同)是在一个类中定义同名方法,方法名相同,如果参数个数不同一定构成重载。个数相同看参数类型,参数类型不同也构成重载。
重写:参数列表与被重写方法的参数列表必须完全相同,返回类型可以不相同,但是必须是父类返回类型的派生类。访问权限不能比父类中被重写的方法的访问权限更低。 声明为final不能被重写。 声明为static的方法不能被重写,只能说是声明。
重写的意义:在不修改源码的前提下,进行功能的修改和扩展,体现了依赖倒转原则,
面向修改关闭,面向扩展开放。
2、面向对象
面向对象是一种思想,强调的是结果,而不用去关注具体实现的过程,相对于面向过程,我们可以由原来问题的执行者变为指挥者,进而把生活中很多复杂的问题变得简单化。
面向对象三大基本特征: (如果问特征就是4个,还有个抽象。抽象主要目的是把类行为和实现细节分离开)
封装:封装是隐藏对象的属性和实现细节,仅仅对外提供公共的访问方式,比如类和方法。 好 处:提高安全性和重用性和可维护性。
继承:继承是从已有的类中派生出新的类,新的类能吸收已有类的数据属性和行为,并扩展新的能力.
多态:指同一个实体同时具有多种形式,即同一个对象,在不同时刻,代表的对象不一样,指的是对象的多种形态。
多态的特点:
- 多态的前提1:是继承
- 前提2:要有方法的重写
- 父类的引用指向子类的对象
- 多态中,编译看左边,运行看右边 (编译看左就相当于看父类是不是调用子类特有的方法了,调用了就报错。运行看右是子类如果重写了父类的方法就调用子类的)
多态的出现是为了统一调用标准,向父类看齐。 父类提供的功能才能用,子类特有的功能用不了。 多态的好处:多态可以让我们不用关心某个对象具体是什么类型,就可以使用该对象的一些方法; 提高了程序的扩展性和可维护性。
1、类和对象
类是一类事物的抽象。
有可能衍生类和对象的关系?
类是对象的模板,对象是类的一个具体实例;类是抽象的,对象是具体的。通过类来描述一类事物,用成员变量描述事物的属性,用方法描述事物的行为。
2、static
是java中的一个关键字,用来修饰成员
static关键字主要有以下四种使用场景:
1、修饰成员变量和成员方法 (通过类名.xxx直接调用)
2、静态代码块
3、修饰类(只能修饰内部类)
4、静态导包(用来导入类中的静态资源,1.5之后的新特性)
3、final
是java中的一个关键字,final可以修饰 类,不能被继承;修饰方法,不能被重写;修饰成员变量,就变成了常量,值不能被改变。
4、静态变量和实例变量的区别
实例变量属于某个对象的属性,必须创建了实例对象,其中的实例变量才会被分配空间,才能使用这个实例变量。静态变量不属于某个实例对象,而是属于类,所以也称为类变量,只要程序加载了类的字节码,不用创建任何实例对象,静态变量就会被分配空间,静态变量就可以被使用了。总之,实例变量必须创建对象后才可以通过这个对象来使用,静态变量则可以直接使用类名来引用。
5、异常
异常是一些用来封装错误信息的对象。
2.6、抽象类
概念:Java中可以定义被abstract关键字修饰的方法,这种方法只有声明,没有方法体,叫做抽象方法。Java中可以定义被abstract关键字修饰的类,被abstract关键字修饰的类叫做抽象类。
- 如果一个类含有抽象方法,那么它一定是抽象类
- 抽象类中的方法实现交给子类来完成
- 抽象类不能被实例化
特点
- abstract 可以修饰方法或者类
- 被abstarct修饰的类叫做抽象类,被abstract修饰的方法叫做抽象方法
- 抽象类中可以没有抽象方法
- 如果类中有抽象方法,那么该类必须定义为一个抽象类
- 子类继承了抽象类以后,要么还是一个抽象类(就是让下一个继承呗,自己变成个抽象子类),要么就把父类的所有抽象方法都重写
- 多用于多态中
- 抽象类不可以被实例化
abstract注意事项
private:被私有化后,子类无法重写,与abstract相违背。
static:静态优先于对象存在,存在加载顺序问题。
final:被final修饰后,无法重写,与abstract相违背。
2.7、接口
接口不是类,没有构造方法。 接口没有成员变量,因为默认都变成了静态常量
public final static xxx
接口概念
与之前学习过的抽象类一样,接口( Interface )在Java中也是一种抽象类型,接口中的内容是抽象形成的需要实现的功能,接口更像是一种规则和一套标准.
接口特点
接口中的方法全部都是抽象方法(当然后面好像改了,可以加…)(抽象方法是省略了 public abstract )
接口和类之间可以多实现,接口与接口之间可以多继承
接口是对外暴露的规则,是一套开发规范
接口提高了程序的功能拓展,降低了耦合性
注意点:
实现接口的类调用构造方法的时候会先调用父级构造方法,而接口是没构造方法。结论:如果一个类没有明确指定父类,那么默认继承顶级父类Object。
3、JAVA-API
1、String
String底层是一个封装的char[]数组,字符串不可变,被final修饰,是个常量。
3.2、【 == 和 equals() 的区别 】
== :如果比较的是基本数据变量,那么就是比较两个变量保存的数据是否相等; 如果比较的是引用数据变量:比较两个对象的地址值是否相等,即两个引用是否指向同一个对象实体。
equals() :只能适用于引用数据类型, Object类中定义的equals()和的作用都是相同的:比较两个对象的地址值是否相同,即两个引用是否指向同一个对象实体; 但是,像String、Date、File、包装类都重写了Object类中的equals()的方法,重写以后,比较的是实体内容(值)是否相同。
通常情况下,我们自定义的类 如果使用equals() 的方法的话,也通常是比较两个对象的实体呢内容是否相同,那么,我们就需要Object类中的equals()方法进行重写。
3.3、【 String/StringBuilder/StringBuffer 】
为什么用 stringbuilder比 string 拼接字符串更快?
因为strin是定长的,所以拼接是返回新的对象,所以string底层拼接字符串用Stringbuilder,但是为什么还是不推荐用string拼接呢,因为是在循环里,那直接用的话每次都会创建Stringbulider,不行,那肯定是用StringBuilder拼接在循环外创建,只要创建一次。
当然如果纯粹是 String res = “hello” + “a” + “b” + “d”; 没变量,这是创建一个对象(编译器优化的,我也不知道为什么),这个不会调用StringBuilder.
Stringbuilder和StringBuffer的区别?
一个线程不安全,一个线程安全。 本质上都是在调用父类抽象类AbstractStringBuilder来干活,只不过Buffer把代码加了同步关键字,使得程序可以保证线程安全问题。
4、Integer
创建对象:
方式一: new Integer(5);
方式二: Integer.valueOf(5);
Integer类中包含256个Integer缓存对象,范围是 -128~127
使用valueOf()时,如果指定范围内的值,直接访问缓存对象不新建;如果指定范围外的值,直接新建对象。 注意:是方式二才行,方式1是行不通的,比如跟 Integer/int i = 5;比,方式一是false,方式二是true。
5、自动装箱和拆箱
自动装箱:把 基本类型 包装成对应的 包装类型 的过程
Integer a = 5;//a是引用类型,引用了包装对象的地址。
编译器会完成对象的自动装箱:Integer a = Integer.valueOf(5);
自动拆箱:从包装类型的值,自动变成 基本类型的值
int i = a;//a现在是包装类型,没法给变量赋值,需要把5取出来。
编译器会完成自动拆箱:int i = a.intValue();
6、IO流
流:方便理解,我们可以把数据的读写操作抽象成数据在"管道"中流动。
字节流:针对二进制文件
File
InputStream
FileInputStream
BufferedInputStream
ObjectInputStream
OutputStream
FileOutputStream
BufferedOutputStream
ObjectOutputStream
字符流:针对文本文件
Reader
FileReader
BufferedReader
InputStreamReader
Writer
FileWriter
BufferedWriter
OutputStreamWriter
PrintWriter一行行写出
字节输出流 不加缓冲流的话如果没关闭流的话,数据还是会存在对应文件里,因为是读了就写进去。
多去温习代码。
7、序列化与反序列化
定义
无论何种类型的数据,都是以二进制的形式在网络上传送,为了由一个进程把Java对象发送给另一个进程,需要把其转换为字节序列才能在网络上传送,把Java对象转换为字节序列的过程就称为对象的序列化,将字节序列恢复成Java对象的过程称为对象的反序列化
作用
作用就是,把对象保存起来。为了不同jvm之间共享实例对象的一种解决方案
温习代码
8、Collection ------List 、Set
arraylist 和hashmap看
============================================================================
Collection接口
List 接口【数据有下标,有序,可重复】
ArrayList子类
LinkedList子类
vector
Set 接口【数据无下标,无序,不可重复】
HashSet子类
Map 接口【键值对的方式寸数据】
HashMap子类
HashTable子类
LinkedHashMap子类
arraylist内部数组默认的初始容量是10,如果不够会以1.5倍的容量增长(后续深入Arraylist)
ArrayList 与LinkedList区别简要概述
Arraylist: 底层是数组结构,查询快(可以通过下标查),增删慢,适合查询较多场景
LinkedList:底层是链表结构,查询慢,增删快,适合增删操作较多的场景。(注意:这里说的慢是指数据量大时,查中间部分慢,首位操作还是快的。
ArrayList的JDK1.8之前与之后的实现区别?
JDK1.7:ArrayList像饿汉式,直接创建一个初始容量为10的数组
JDK1.8:ArrayList像懒汉式,一开始创建一个长度为0的数组,当添加第一个元素时再创建一个始容量为10的数组
9、Map
![img](https://i-blog.csdnimg.cn/blog_migrate/490622a1cae89145b70759c09148204a.png)
【 HashMap 】重点!!!!!!这些不够还要去看
1,初始化大小是16,如果事先知道数据量的大小,建议修改默认初始化大小。 减少扩容次数,提高性能 ,这是我一直会强调的点
2,最大的装载因子默认是0.75,当HashMap中元素个数达到容量的0.75时,就会扩容。 容量是原先的两倍
3,HashMap底层采用链表法来解决冲突。 但是存在一个问题,就是链表也可能会过长,影响性能
于是JDK1.8,对HashMap做了进一步的优化,引入了红黑树。
当链表长度超过8,且数组容量大于64时,链表就会转换为红黑树
当红黑树的节点数量小于6时,会将红黑树转换为链表。
因为在数据量较小的情况下,红黑树要维护自身平衡,比链表性能没有优势。
HashMap 和HashTable区别?
JDK1.8 主要区别如下:
- 线程安全性不同。HashMap线程不安全;Hashtable 中的方法是Synchronize的。
- key、value是否允许null。HashMap的key和value都是可以是null,key只允许一个null;Hashtable的key和value都不可为null。
- 迭代器不同。HashMap的Iterator是fail-fast迭代器;Hashtable还使用了enumerator迭代器。
- hash的计算方式不同。HashMap计算了hash值;Hashtable使用了key的hashCode方法。
- 默认初始大小和扩容方式不同。HashMap默认初始大小16,容量必须是2的整数次幂,扩容时将容量变为原来的2倍;Hashtable默认初始大小11,扩容时将容量变为原来的2倍加1。
- 父类不同。HashMap继承自AbstractMap;Hashtable继承自Dictionary。
- 是否有contains方法。HashMap没有contains方法;Hashtable包含contains方法,类似于containsValue。
HashMap和LinkedHashmap区别?
HashMap无序,存值的时候会根据key的hashCode()来计算存储的位置(位置是散列的,所以说其无序)。
LinkedHashMap有序,就是链表+散列表(hash表)的结构,其底层采用了Linked双向链表来保存节点的访问顺序,所以保证了有序性。
============================================================================
额外话:TreeMap 默认排序规则:按照key的字典顺序来排序(升序),当然,也可以自定义排序规则:要实现Comparator接口。
10、进程与线程(看视频)
10.1进程
进程的概念:
进程就是正在运行的程序,它代表了程序所占用的内存区域。是系统进行资源分配和调度的一个独立单位。
高并发:多个进程抢占公共资源
并行:多个CPU同时处理不同的进程
10.2线程
线程是操作系统OS能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。
线程的状态点击看
notify 和notifyAll 区别
个人理解;虽然两者都是都唤醒所有等待,当前获得锁的线程不会立马释放锁,退出syxxx代码块后,去抢锁,当有线程抢到了锁,其他线程阻塞在wait状态,即使该线程释放了,没有继续唤醒那也没用。而notifyAll,是在该基础上,所有线程会继续抢,直到所有等待的都执行完毕。 简要说明:都唤醒,notify的被抢到了锁,其他的就继续wait,直到再次唤醒。而notifyAll不用,会一直抢,直到所有都执行。
同步锁
11、单例模式
饿汉式
public class 饿汉式 {
public static void main(String[] args) {
TestSingle single1 = TestSingle.getSingle();
TestSingle single2 = TestSingle.getSingle();
System.out.println(single1==single2); //true
}
}
class TestSingle{
private TestSingle(){};
static private TestSingle single = new TestSingle();
static public TestSingle getSingle(){
return single;
}
}
懒汉式
public class 懒汉式 {
public static void main(String[] args) {
MySingle single1 = MySingle.getMysingle2();
MySingle single2 = MySingle.getMysingle2();
System.out.println(single1==single2); //true
}
}
class MySingle{
private MySingle(){};
static private MySingle single;
static public MySingle getMysingle2(){
if(single == null){
single = new MySingle();
}
return single;
}
}
深拷贝和浅拷贝
浅拷贝是指向被复制的内存地址,而深拷贝是创建新的内存地址用于存放复制的对象。(就像clone()和序列化)
浅拷贝:只复制一个对象,对象内部存在的指向其他对象数组或者引用则不复制
深拷贝:对象,对象内部的引用均复制
例子:
Person p = new Person(23, "zhang");
Person p1 = (Person) p.clone();
String result = p.getName() == p1.getName()
? "clone是浅拷贝的" : "clone是深拷贝的";
System.out.println(result);
打印结果为:clone是浅拷贝的。
BIO、NIO、AIO
13、反射
反射定义:
反射机制是在程序运行状态中, 对于任意一个类, 都能够知道这个类的所有属性和方法; 对于任意一个对象, 都能够调用它的任意一个属性和方法。依靠此机制,可以动态的创建一个类的对象和调用对象的方法。
反射获取对象方法:
然后,有三种通过反射获取对象的方法。
Class c1 = Class.forForName("全类名");
Class c2 = 类.class;
Class c3 = 对象.getClass();
反射优缺点:
优点就是增加灵活性,可以在运行时动态获取对象实例。
缺点是反射的效率很低,而且会破坏封装,通过反射可以访问类的私有方法,不安全。
运用:
Spring 框架的 IOC 基于反射创建对象和设置依赖属性。
mybatis框架通过读取sql,得到字段名称(与属性名相同),并用反射的方式将User对象创建出来,之后调用其set方法进行参数注入。最后放到List中。
反序列化底层也是反射
设计模式:单例模式、工厂模式、代理模式等
反射的运用之动态代理
java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
java动态代理实现代理步骤:
a定义被代理类:接口及接口实现类
b定义代理类,代理类构造器传入被代理类,且需要实现InvocationHandler接口的类并重写invoke方法
c生成被代理的类的实例:调用 Proxy.newProxyInstance(被代理的类.getClass().getClassLoader(),
被代理的类.getClass().getInterfaces(),
InvocationHandler的实现类);
注意:newProxyInstance返回的是接口类型,所以java动态代理要求被代理类实现接口。
d被代理的类的实例调用需要执行的方法
JDK动态代理和CGLIB字节码生成的区别?
(1)JDK动态代理只能对实现了接口的类生成代理,而不能针对类
(2)CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法
因为是继承,所以该类或方法最好不要声明成final
14、内部类
特点
-
内部类可以直接访问外部类中的成员,包括私有成员
-
外部类要访问内部类的成员,必须要建立内部类的对象
-
在成员位置的内部类是成员内部类
-
在局部位置的内部类是局部内部类
===============================================================
成员内部类被Private修饰以后,无法被外界直接创建创建对象使用,所以可以创建外部类对象,通过外部类对象间接访问内部类的资源
使用内部类的好处:隐藏,可以不想让别人轻易的访问这个类。
(匿名内部类:“其实是继承该类或者实现接口的子类匿名对象)
好处:java是不可以实现多继承的,一次只能继承一个类,我们学习接口的时候,有提到可以用接口来实现多继承的效果,即一个接口有多个实现,但是这里也是有一点弊端的,那就是,一旦实现一个接口就必须实现里面的所有方法,有时候就会出现一些累赘,但是使用内部类可以很好的解决这些问题
例子: 这是间接实现 多个继承,因为java只能单继承 。
public class Demo1 {
public String name() {
return "BWH_Steven";
}
}
public class Demo2 {
public String email() {
return "xxx.@163.com";
}
}
public class MyDemo {
private class test1 extends Demo1 {
public String name() {
return super.name();
}
}
private class test2 extends Demo2 {
public String email() {
return super.email();
}
}
public String name() {
return new test1().name();
}
public String email() {
return new test2().email();
}
public static void main(String args[]) {
MyDemo md = new MyDemo();
System.out.println("我的姓名:" + md.name());
System.out.println("我的邮箱:" + md.email());
}
}
例子:针对接口的,好处显而易见,当接口里有多个方法,而你仅仅只想要实现其中一两个,但是你实例化这个大类会暴露出很多的方法,这时你用匿名内部类在里面实现后掉你想要用的方法,这样就不会暴露出那么多。
interface Inner {
public abstract void show();
}
class Outer {
public void method(){
new Inner() {
public void show() {
System.out.println("HelloWorld");
}
}.show();
}
}
class Test {
public static void main(String[] args) {
Outer o = new Outer();
o.method();
}
}