常遗忘的JAVA基础知识,整理更新
接口
1 成员变量其实是常量,格式:
1 [public ][static ][final ] 数据类型 名称 = 数据值:
注意
常量必须赋值,不可变 常量名完全大写
2 接口中最重要部分为抽象方法,格式:
1 [public ][abstract ]返回值类型 方法名称(参数列表);
注意:
实现类必须覆盖重写接口所有的抽象方法,除非实现类是抽象类
3 从Java8开始,接口里允许定义默认方法,格式:
1 [public ] default 返回值类型 方法名称(参数列表){方法体}
注意:
默认方法可以被覆盖重写
4 从Java6 开始,接口里允许定义静态方法,格式:
1 [public ] static 返回值类型 方法名称(参数列表){方法体}
注意:
应该通过接口名称进行调用,不能通过实现类对象调用接口静态方法
5 从Java 9开始,接口里允许定义私有方法,格式:
1 普通私有方法:
1 private 返回值类型 方法名称(参数列表){方法体}
5.2 静态私有方法:
1 private static 返回值类型 方法名称(参数列表){方法体}
注意:
private的方法只有接口自己才能调用,不能被实现类或别人使用
6 使用接口注意事项:
接口没有静态代码块或者构造方法的
父类唯一,但可以实现多个接口
实现的多个接口存在重复方法,覆盖重写一次即可。
没有重新接口中的所有抽象方法,实现类必须定义为抽象类
实现的接口中存在重复默认方法,必须对冲突的默认方法进行覆盖重写。
父类方法与接口方法冲突,优先父类中的方法
多态
1 访问成员变量的两种方式:
直接通过对象名称访问成员变量:等号左边是谁,优先用谁,没有则往上找
间接通过成员方法访问成员变量:该方法属于谁,优先用谁,没有则往上找
2 成员方法的访问方式:
new谁用谁,没有则上找,直到object
3 对象向上转型(就是多态写法) 格式:
含义:右侧创建一个子类对象,把它当作父类来用。
注意
向上转型一定是安全的
4 对象向下转型(还原的动作,类似于强制类型转换) 格式:
1 子类名称 对象名称 = (子类名称) 父类对象;
含义:将父类对象,还原成本来的子类对象
例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Animal animal = new Cat(); animal.eat(); Cat cat = (Cat) animal; Cat.catchMouse(); Dog dog = (Dog) animal; animal instanceof Cat () animal instanceof Dog ()
注意:
1 向下转型必须保证对象创建时的类型一致,否则:ClassCastException 2 向下转型一定要通过instanceof判断前面的对象能不能当作后面类型的实例
关键字
final
1 修饰类 该类无法作为父类==所有成员方法都不能重写
2 修饰方法 该方法无法覆盖重写
3 修饰一个局部变量 基本类型变量值不能更改,引用类型地址不可改变
4 修饰成员变量
基本类型变量值不能更改,必须手动赋值,默认值不可用
赋值方式: 直接赋值 ,构造方法赋值
必须所有重载的构造方法,都对final修饰的成员变量赋值.
注意:
abstract final无法同时使用,因为矛盾
访问修饰符
public
protected
(default)
private
同一个类
Y
Y
Y
Y
同一个包
Y
Y
Y
N
不同包子类
Y
Y
N
N
不同包非子类
Y
N
N
N
内部类
成员内部类
内部类调用方法 1 间接方法: 外部类使用内部类.然后调用外部类方法 2 直接方法
1 外部类名称.内部类名称 对象名 = new 外部类名称().内部类名称();
局部内部类
1 类定义在一个方法内部,就是局部内部类,只有当前所属方法才能使用它.
2 格式:
1 2 3 4 5 6 7 8 9 10 11 public class Outer { public void methodOuter () { class Inner { int n = 10 ; System.out.println(n); } Inner inner = new Inner(); inner.methodInner(); } }
通过调用methOuter()方法创建并调用局部内部类
3 局部内部类final问题
局部内部类访问所在方法的局部变量必须是有效的finall
从java8开始final可以省略,但是变量值不能改变
原因:
new出的方法在堆内存中
局部变量跟着方法.在栈内存中
方法运行结束后,立即出栈,局部变量消失.
new出的对象会在堆当中持续存在.直到gc
小结内部类于权限修饰符
外部类: public /(default) 成员内部类:四者皆可 局部内部类: (default)
匿名内部类 (非匿名对象)
接口的实现类只使用一次得情况【new 对象的时候】。 格式:
1 2 3 接口名称 对象名 = new 接口名称(){ }
解析
new代表创建对象的动作
接口名称就是匿名内部类需要实现的哪个接口
{…}为匿名内部类内容
注意:
匿名内部类:在创建对象 的时候,只能使用一次
匿名对象:在调用方法 得时候,只能调用一次,若想多次调用,必须是非匿名对象
匿名内部类是省略了实现类 ,匿名对象省略得是名称
线程
创建多线程
方法1:创建Thread的子类,并重写run方法
JDK所给例子:计算大于某一规定值的质数的线程可以写成:
1 2 3 4 5 6 7 8 9 10 11 class PrimeThread extends Thread { long minPrime; PrimeThread(long minPrime) { this .minPrime = minPrime; } public void run () { . . . } }
创建并启动一个线程
1 2 PrimeThread p = new PrimeThread(143 ); p.start();
方法二:声明实现Runnable 接口的类。该类然后实现run 方法
1 2 3 4 5 6 7 8 9 10 11 class PrimeRun implements Runnable { long minPrime; PrimeRun(long minPrime) { this .minPrime = minPrime; } public void run () { . . . } }
创建并启动一个线程
1 2 PrimeRun p = new PrimeRun(143 ); new Thread(p).start();
两种实现方式区别: 实现Runable的优势
避免了单继承的局限: 继承了Thread无法再继承其他类
增强了程序的扩展性,降低了程序的耦合性:实现Runable接口的方法,把设置线程任务和开启新线程进行了分离(解耦), 实现类中重写了run方法:用来设置线程任务创建Thread类对象 ,调用start方法:开启新线程
线程安全问题
解决方法1:同步代码块
同步技术:
Thread们抢CPU执行权,抢到后进入run()方法。发现同步锁,就会获得锁对象并执行,执行结束后归还锁对象。 若没有发现锁对象,进入到阻塞状态等待,直到发现锁对象并执行。
频繁判断锁状态,消耗资源
格式:
1 2 3 synchronized (同步锁){ 需要同步的共享数据代码块 }
同步锁 :
锁对象 可以为任意类型
多个线程对象,要使用同一把锁
注意:
通过代码块中的锁对象,可以锁定任意的对象
必须保证多个线程使用的锁对象是同一个
锁对象的作用:把同步代码块锁住,只让一个线程再同步代码块中执行
例:再Runable实现类中使用同步代码块,并new 3 个Thread 调用Runable的同一个子类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class RunableImpl implements Runnable { int date = 10 ; @Override public void run () { while (true ){ if (date>0 ){ try { Thread.sleep(10 ); }catch (InterruptedException e){ e.printStackTrace(); } System.out.println(date); date--; } } } }
使用同步代码块使其正常打印
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class RunableImpl implements Runnable { int date = 10 ; Object obj = new Object(); @Override public void run () { while (true ){ synchronized (obj){ if (date>0 ){ try { Thread.sleep(10 ); }catch (InterruptedException e){ e.printStackTrace(); } System.out.println(date); date--; } } } } }
main:
1 2 3 4 5 6 7 Runnable r = new RunnableImpl(); Thread t0 = new Thread(run); Thread t1 = new Thread(run); Thread t2 = new Thread(run); t0.start(); t0.start(); t0.start();
output
解决方法2:同步方法
格式:
1 2 3 public synchronized void method () { 线程安全问题代码 }
1 2 3 public static synchronized void method () { 线程安全问题代码 }
同步锁:
非static方法,同步锁为this。
static方法,当前方法坐在类的字节码对象(类名.class)
例:再Runable实现类中使用同步方法,并new 3 个Thread 调用Runable的同一个子类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class RunableImpl implements Runnable { int date = 10 ; @Override public void run () { while (true ){ datesub(); } } public synchronized void dateSub () { if (date>0 ){ try { Thread.sleep(10 ); }catch (InterruptedException e){ e.printStackTrace(); } System.out.println(date); date--; } } }
解决方法3:Locl锁
优点: 比使用synchronized 方法和语句可提供的更广泛的锁定操作。 Lock接口中的方法:
lock()```:获取锁. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 ```void unlock()```:释放锁 ```java public class RunableImpl implements Runnable { private final ReetrantrantLocl lock = new ReentrantLock(); int date = 10; @Override public void run() { while(true){ lock.lock(); if(date>0){ //sleep造成数据异常(重复打印,小于0) try{ Thread.sleep(10); System.out.println(date); date--; }catch(InterruptedException e){ e.printStackTrace(); }finally{ lock.unlock();//无论如何最后都将释放锁对象 } } } } }
线程状态
线程通信
线程池
程序第一次启动时,创建多个线程,保存在一个集合中 当使用某个线程时候,就可以从集合中取出。
1 2 3 4 5 Thread t = list.remove(0 ); Thread t = Linked.removeFist(); list.add(t) linked.addLast(t);
JDK1.5后内置线程池
java.util.concurrent.Executors:线程池工厂类,用来生成线程池 静态方法:static ExecutorService newFixedThreadPool(int nThread):创建一个可重用固定线程数的线程池 参数:int nThreads:线程池中的线程数量 返回值:ExecutorService:接口的实现类java.util/concurrent.ExecutorService:线程池接口:用来从线程池中获取线程,调用start()方法:执行线程任务submit(Runnable task):提交一个Runnable任务来执行voidshutdown():关闭/销毁线程池的方法 使用:
创建线程池
实现Runnable借口,重写run,设置线程任务
调用submit()方法,传递线程任务(实现类) 。开启线程,执行run方法
shutdown()销毁线程池(不建议)
1 2 ExecutorService es = Executors.newFixedThreadPool(10 ); es.submit(new RunnableIml());
Lambda表达式
思想: 重视结果,不重视过程
使用前提:
使用Lambda必须具有借口,切借口有且仅有一个方法。
使用Lambda必须具有上下文推断。(方法参数和局部类型必须为对应的接口类型)
Lambda 表达式的标准格式:(参数列表)->{重写方法的代码}
File
静态成员变量:
String pathSeparator```路径分隔符```";"```或者```":"```(linux,windows) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 ```static char pathSeparator```:路径分隔符```';'```或者```‘:’```(linux,windows) ```static String Separator```路径分隔符```"/"```或```“\”```(linux,windows) ```static char Separator```:路径分隔符```'/'```(linux,windows) File.listFiles(过滤器) ## IO流 | | 输入流 | 输出流 | | ------ | ----------- | ------------ | | 字节流 | InputStream | Outputstream | | 字符流 | Reader | Writer | ### 字节流 #### 字节输出流(程序(内存)->设备(硬盘)) ```java byte b = 65; byte[] bytes = {65,66,67} byte[] bHello = "你好".getBytes(); FileOutputStream fos = new FileOutputStream(File file,boolean append); //append:true,不新建文件,追加写。false:重新写 //fos.write(b);//单字节 //fos.write(bytes);字节数组 fos.write(bHelle);
字节输入流(设备(硬盘)->程序(内存))
1 2 3 4 5 6 7 8 9 10 11 12 13 FileOutputStream fis = new FileinputStream(File file,boolean append); byte [] bytes = new byte [2 ];int len = fis.read(bytes);while (len = fis.read(bytes)!=-1 ){ } while (len = fis.read()!=-1 ){ }
字符流
字符输入流
1 2 3 4 5 6 7 8 9 FileReader fr = new FileReader(File file); int len = 0 ;while ((len = fr.read())!=-1 ){ } char [] cs = new char [1024 ];while ((len=fr.read(cs))!=-1 ){ }
字符输出流
将数据写入内存缓冲区(字符转为字节) 1 2 3 4 5 6 >**```FileWriter.flush()```刷新到文件中!** >flush() 和close()区别:flush()后流可继续使用,close()后流已经关闭 ```java FileWriter fw = new FilewWriter(File file); fw.wirte(97);
Properties属性集
properties集合是一个双列集合,key和value默认都是字符串 properties集合操作字符串的方法:
setProperty(String key,String values)``` 调用Hashtable的方法put. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 ```String getProperty(String ket)``` 通过key找到value值。此方法相当于```get(key)```方法 ```Set<String> stringPropertyNames()``` 返回此属性列表的键值,其中该键值及其对应值是字符串,此方法相当于Map集合的```keySet()```方法 #### store 可使用Properties集合中的store方法,把集合中的临时数据,持久化写入到硬盘中 ```void store(OutStream out,String comments)``` ```void store(Writer writer,String comments)``` tips: >字节输出流和注释都不可使用中文 >字符输出流可以使用中文 使用步骤: 1. 创建Properies集合对象,添加数据 2. 创建字节/字符输出流,构造方法中绑定要输出的目的地 3. 使用Properties集合中的方法store,把集合中的临时数据,持久化写入到硬盘中 4. 释放资源 #### load 可使用Properties集合中的load方法,把硬盘中的文件,读到集合当中 ```void load(InputStream inSteam)``` ```void load(Reader reader)``` tips: >文件中的键值可以使用空格,=,其他 >使用#进行注释 >键与值默认都是字符串不再需要加引号 使用步骤: 1. 创建Properies集合对象 2. 使用Properties集合中的方法load,读取保存键值的文件 3. 遍历Properties集合 ### 缓冲流 提高效率 构造方法都有两种,默认大小缓冲区、指定缓冲区 #### 字节缓冲输出流 BufferedOutputStream 1. 创建FileOutputStream对象,构造方法中绑定要输出的目的地。 2. 创建BuffereOutputStream对象,构造方法中传递FileOutputStream对象,提高效率。 3. 使用BuffereOutputStream对象的writer方法。把数据写入内部缓冲区 4. 使用BuffereOutputStream对象的方法flush,把内部缓冲区的数据,刷新到文件 5. 释放资源(会先调用flush,因此第四步可省略) ```java FileOutputStream fos = new FileOutputStream(File); BufferedOutputStream bos = new BufferedOutputStraeam(fos); bos.write(“”.getBytes()); bos.flush(); bos.close();
创建FileInputStream对象,构造方法中绑定要输出的目的地。
创建BuffereInputStream对象,构造方法中传递FileInputStream对象,提高效率。
使用BuffereInputStream对象的read方法。
释放资源
1 2 3 4 5 6 7 8 9 10 11 12 13 14 FileInputStream fis = new FileInputStream(File); BufferedInputStream bis = new BufferedInputStraeam(fis); byte [] bytes = new bytes[1024 ];int len = 0 ;while ((len = bis.read(bytes))!=-1 ){ } bis.close();
字符缓冲输入流 BufferedReader
末尾读不到返回null
创建FileReader对象,构造方法中绑定要输出的目的地。
创建BuffereReader对象,构造方法中传递FileReader对象,提高效率。
使用BuffereReader对象的read()方法。
释放资源
1 2 3 4 FiledReader fr = new FileReader(File); BufferedReader br = new BufferedReader(fr); br.readLine(); bos.close();
字符缓冲输出流 BufferedWriter
创建FileWriter对象,构造方法中绑定要输出的目的地。
创建BuffereWriter对象,构造方法中传递FileWriter对象,提高效率。
使用BuffereWriter对象的writer方法。
调用flush
释放资源
1 2 3 4 5 6 FileWriter fw = new FileWriter(File); BufferedWriter bw = new BufferedWriter(fw); bw,writer(str); bw.newLine(); bw.flush(); bis.close();
转换流
解决不同编码问题
OutputStreamWriter类
构造方法OutputStreamWriter(OutputStream out)
out,, String charsetName)```: charsetName 为编码类型,不区分大小写 1 2 3 4 5 6 7 8 9 10 11 1. 创建OutputStreamWriter对象,构造方法中传递字节输出流和指定的编码表名称 2. 使用OutputStreamWriter对象的方法write,把字符转换为字节存储在缓冲区 3. 使用OutputStreamWriter对象的flush,把内存中的字节刷新到文件 4. 释放资源 ```java OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(File),"UTF-8"); osw.writer("你好”); osw.flush(); osw.close();
InputStreamReader(OutputStream out)
out,, String charsetName)```: charsetName 为编码类型,不区分大小写 1 2 3 4 5 6 7 8 9 10 11 12 1. 创建InputStreamReader对象,构造方法中传递字节输入流和指定的编码表名称 2. 使用InputStreamReader对象的方法read 3. 释放资源 ```java InputStreamReader isr = new InputStreamReader(new FileInputStream(File),"UTF-8"); int len = 0; while((len=isr.read())!=-1){ //len } osw.close();
序列化&反序列化
序列化:文件写入对象 反序列换:读取文件的对象
类必须实现Serializable接口。没有则抛出异常
序列化ObjectOutputStream
创建ObjectOutputStream对象,构造方法中传递字节输出流
使用ObjectOutputStream对象的writerObject,写入到文件
资源释放
1 2 3 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(File)); oos.wirteObject(new Studient()); oos.close();
创建ObjectInputStream对象,构造方法中传递字节输出流
使用ObjectInputStream对象的readObject
资源释放
1 2 3 4 5 ObjectInputStream ois = new ObjectInputStream(new FileInputStream(File)); Object o = ois.readObject(); oos.close(); Student s = (Student) o;
transient 瞬态关键字
修饰的成员变量不能被序列化。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 非```static```修饰的成员变量,可以使用transient使其不能被序列化 #### 其他 序列化UID; 每次修改类的定义都会给class文件生成一个新的序列号 导致修改类后反序列化异常 解决:Serializable接口中的```serialVersionUID```` ```private static final long serialVersionUID = 1L;``` 序列化集合: ### 打印流PrintStream >只负责数据输出,不负责读取 >不会产生IO异常 >如果使用write方法,打印编码表 >如果使用print/println写出的数据原样输出 ```java PrintStream ps = PrintStream(File); ps.write(97); ps.println(97); // System.setOut(ps);//更改打印流目的地 // System.out.println();//不会出现在控制台,而是在FIle文件
输出: a 97
网络编程
TCP通信
客户端
Socket
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Socket socket = new Socket(String host,int port); OutputStream os = socket.getOutputStream(); os.write(str.getBytes()); InputStream is = socket.getInputStream(); byte [] bytes = new byte [1024 ];int len = is.read(bytes);
服务器端
ServerSocket
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ServerSocket serve = new ServerSocket(int port); Socket socket = server.accept(); InputStream is = socket.getInputStream(); byte [] bytes = new byte [1024 ];int len = is.read(bytes);OutputStream os = socket.getOutputStream(); os.write(str); socket.close(); os.close();
函数式
函数式接口
适用于Lambda表达式
有且只有一个抽象方法的接口 可以包含其他方法:默认,静态,私有 注解 @FuncationalInterface检测是否为函数式接口
函数式接口的使用
作为方法的参数和返回值。
1 2 3 4 5 6 7 8 9 10 11 12 public interface MyInterFace { void show () ; } void show (MyInterFace interface) { interface.show(); } public static void main (String[] args) { show(()->System.out.println(str)); }
函数式编程
Lambda:延迟加载。只是将Lambda表达式作为参数传递。并不会直接执行表达式的方法。在调用时才会执行其方法
常用函数式接口
Junit测试
定义测试类
定义测试类型
导入import org.junit.Test;
给方法加上@test
使用断言Assert判断结果
@Before:所有测试方法执行前都会自动调用该方法。@After:所有测试方法执行后都会自动调用该方法。 ``
反射
将类的各个部分封装成其他对象
获取Class对象的方法:
Class.forName("全类名"):常用于配置文件
类名.class:常用于当做参数传递
对象.getClass():常用于获取对象的字节码
使用Class对象
获取成员变量:关键词Fields
获取构造方法:关键词Constructor
获取成员方法:关键词Method
获取类名:getName()
反射案例
将需要创建的对象的全类名和需要执行的方法定义在配置文件中
加载配置文件
使用反射加载类文件到内存
创建对象
执行方法
注解
JDK3种:
@Override:重写
@Deprecated:标注过时
@SuppressWarnings(String):压制所有警告.all
自定义注解
1 2 3 public @inferface MyAnno{}
注解本身就是接口,继承了Annotation接口
属性: 接口中的抽象方法
属性的返回值有以下类型
基本数据类型
Sting
枚举
注解
以上类型的数组
定义了属性,使用时需要给属性赋值
定义属性时,使用了default关键字给属性默认初始化,使用注解时可以不赋值
如果仅有一个属性需要赋值,并且属性名称为value则可以省略,直接定义即可
元注解
@Target:描述注解能够作用的位置
@Retention:描述注解被保留的阶段
Documented:描述注解是否被抽取到api文档中
@Inherited:描述注解是否被子类继承
解析注解
1 2 3 4 5 6 7 8 @MyAnno(name = "didi",age = 12) public class test { public static void main (String[] args) { Class<test> reflect = test.class; MyAnno ano = reflect.getAnnotation(MyAnno.class); String name = ano.name(); } }
JDBC
6步:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 public class EmployeeDaoImpl implements EmployeeDao { String driver = "com.mysql.cj.jdbc.Driver" ; Statement state; ResultSet rs; String url = "jdbc:mysql://127.0.0.1:3306/newsql" ; String user = "root" ; String passWord = "52137" ; List<Employes> employesList = new ArrayList<>(); @Override public List<Employes> findAll () { Connection conn = null ; try { Class.forName(driver); conn = DriverManager.getConnection(url,user,passWord); state = conn.createStatement(); String sqlSelectAll = "select * from emp" ; rs = state.executeQuery(sqlSelectAll); while (rs.next()){ int empNo = rs.getInt("e_no" ); String empName = rs.getString("e_name" ); String empJob = rs.getString("e_job" ); Date hireData = rs.getDate("e_firedate" ); double empSal = rs.getDouble("e_sal" ); double empComm = rs.getDouble("e_comm" ); int empMgr = rs.getInt("e_mgr" ); int depNo = rs.getInt("e_deptno" ); Employes emp = new Employes(empNo, empName, empJob,empMgr, hireData, empSal, empComm, depNo); employesList.add(emp); } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException throwables) { throwables.printStackTrace(); }finally { if (conn!=null ){ try { conn.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } try { state.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } try { rs.close(); } catch (SQLException throwables) { throwables.printStackTrace(); } } return employesList; } }