java基础
复习一下java,以下是java复习阶段的一些知识点
1、+=扩展
public class self_plus_test {
public static void main(String[] args) {
short s = 1;
s+=1;
System.out.println(s);
}
}
分析:s += 1逻辑上可以看成是s = s + 1,计算结果被提升为int,再向short赋值时发生错误,但是 s = s + 1进行了两次运算,+=是一个运算符,只运算一次,并带有强制转换的特点,也就是说s += 1就是s = (short)(s + 1),因此编译没有问腿,结果为2.
2、byte
public class constant_variable_test {
public static void main(String[] args) {
byte b1 = 1;
byte b2 = 2;
byte b3 = 1 + 2;
byte b4 = b1 + b2;
System.out.println(b3);
}
byte b3 = 1 + 2; 1 + 2可以确定值在byte的范围内,赋值给b3没有问题,但b1 b2是变量,编译器无法确定具体的值,会将结果以int类型进行处理,但int不能赋值给byte,便易出错。
3、方法的参数问题
方法的参数是基本数据类型时,传递的是值,当方法的参数是引用数据类型时,传递的是地址
4、成员变量的默认值
数据类型 | 默认值 | |
---|---|---|
基本类型 | 整数(byte, short, int, long) | 0 |
浮点(float, double) | 0.0 | |
字符(char) | ‘\u0000’ | |
布尔(boolean) | false | |
引用类型 | 数组、类、接口 | null |
5、线程安全
synchronized解决线程同步问题
5.1 同步代码块
package cn.learnj.ficca.advanced;
public class TicketRunnableImp implements Runnable{
private int ticket = 100;
Object lock = new Object();
@Override
public void run() {
//窗口永远开启
while (true){
synchronized (lock){
if(ticket > 0){//有票
try {
//模拟出票操作,使用sleep模拟出票时间
Thread.sleep(100);//此时仍占有锁,只不过没有cpu执行权
System.out.println("");
}catch (Exception e){
e.printStackTrace();
}
String name = Thread.currentThread().getName();
System.out.println(name + "正在卖" + ticket--);
}//出了同步代码块,归还锁🔐
}
}
}
}
package cn.learnj.ficca.advanced;
public class TicketTest {
public static void main(String[] args) {
//创建线程任务对象
TicketRunnableImp ticket = new TicketRunnableImp();
Thread t1 = new Thread(ticket,"窗口1");
Thread t2 = new Thread(ticket, "窗口2");
Thread t3 = new Thread(ticket, "窗口3");
t1.start();
t2.start();
t3.start();
}
}
5.2 同步方法
非static方法,锁对象是this
static方法,锁对象是当前方法所在类的字节码对象(类名.class)
package cn.learnj.ficca.advanced.ticket.method;
public class RunnableImp implements Runnable {
private int ticket = 100;
@Override
public void run() {
while (true){
sellTicket();
}
}
//同步方法,锁对象是this, 即创建的run对象
private synchronized void sellTicket() {
if(ticket > 0){//有票
try {
//模拟出票操作,使用sleep模拟出票时间
Thread.sleep(100);
System.out.println("");
}catch (Exception e){
e.printStackTrace();
}
String name = Thread.currentThread().getName();
System.out.println(name + "正在卖" + ticket--);
}
}
}
package cn.learnj.ficca.advanced.ticket.method;
public class RunnableTest {
public static void main(String[] args) {
RunnableImp run = new RunnableImp();
Thread t1 = new Thread(run, "窗口1");
Thread t2 = new Thread(run, "窗口2");
Thread t3 = new Thread(run, "窗口3");
t1.start();
t2.start();
t3.start();
}
}
5.3 锁机制
java.util.concurrent.locks.lock
机制提供了比synchronized代码块和synchronized方法更广泛的锁定操作,同步代码块和同步方法具有的功能Lock都有,更体现面向对象思想。
Lock锁也称同步锁,加锁和释放锁方法化。
加同步锁:
public void lock()
释放同步锁:
pubic void unlock()
使用如下:
package cn.learnj.ficca.advanced.lock.ticket;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class lockRunnable implements Runnable{
private int ticket = 100;
Lock lock = new ReentrantLock();
@Override
public void run() {
//窗口永远开启
while (true){
lock.lock();
if(ticket > 0){//邮票
try {
//模拟出票操作,使用sleep模拟出票时间
Thread.sleep(100);
System.out.println("");
}catch (Exception e){
e.printStackTrace();
}
String name = Thread.currentThread().getName();
System.out.println(name + "正在卖" + ticket--);
}
lock.unlock();
}
}
}
package cn.learnj.ficca.advanced.lock.ticket;
import cn.learnj.ficca.advanced.demo01.ticket.TicketRunnableImp;
public class lockTest {
public static void main(String[] args) {
//创建线程任务对象
lockRunnable run = new lockRunnable();
Thread t1 = new Thread(run,"窗口1");
Thread t2 = new Thread(run, "窗口2");
Thread t3 = new Thread(run, "窗口3");
t1.start();
t2.start();
t3.start();
}
}
6、线程池
Java里面线程池的顶级接口是 java.util.concurrent.Executor ,但是严格意义上讲 Executor 并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口java.util.concurrent.ExecutorService
java.util.concurrent.Executors 线程工厂类里面提供了一些静态工厂,生成一些常用的线程池。官 方建议使用Executors工程类来创建线程池对象。
Executors类中有个创建线程池的方法如下:
-
public static ExecutorService newFixedThreadPool(int nThreads) : 返回线程池对象
-
public Future<?> submit(Runnable task) : 获取线程池中的某个线程对象,并执行
Future接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用。
使用线程池中线程对象:
- 1、创建线程池对象
- 2、创建Runnable接口子类对象
- 3、提交Runnable接口子类对象
- 4、关闭线程池(一般情况下不做)
package cn.learnj.ficca.advanced.threadpool; import com.sun.jdi.ThreadReference; public class MyRunnable implements Runnable{ @Override public void run() { System.out.println("我要一个教练!" + Thread.currentThread().getName()); try{ Thread.sleep(2000); }catch (InterruptedException e){ e.printStackTrace(); } System.out.println("教练来了:" + Thread.currentThread().getName()); System.out.println("教我游泳,教完后,教练回到了游泳池"); } }
package cn.learnj.ficca.advanced.threadpool; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadPoolDemo { public static void main(String[] args) { //创建线程池对象 ExecutorService service = Executors.newFixedThreadPool(2); //创建Runnable实例对象 MyRunnable r = new MyRunnable(); /* //手动创建线程的方式 Thread t = new Thread(r); t.start(); */ //从线程池中获取线程对象,然后调用MyRunnable中的run() service.submit(r); //再获取一个线程对象,调用MyRunnable中的run() service.submit(r); service.submit(r); //tips: submit方法调用结束之后程序并不终止,因为线程池控制着线程的关闭 //将使用完的线程归还到线程池中 //结束线程 // service.shutdown(); } }
7、Lambda表达式
即函数式编程,重点在于做什么,而不是怎么做
7.1 Lambda表达式的标准格式为:
(参数类型 参数名称) ‐> { 代码语句 }
7.2 省略规则
- 参数类型可省略
- 仅有1个参数,小括号可省略
- 大括号内仅有一条语句,无论是否有返回值,都可以省略大括号,return关键字及语句分号
7.3 使用前提
-
必须具有接口,且接口中仅有一个抽象方法
tips: 有且仅有一个抽象方法的接口,称为”函数式接口“.
-
必须具有上下文推断,即方法的参数或局部变量类型必须为lambda对应的接口类型,才能使用lambda作为该接口的实例
以下是实现多线程的一个例子:
package cn.learnj.ficca.advanced.lambda;
public class ThreadLambdaTest {
public static void main(String[] args) {
new Thread(() -> {
System.out.println("多线程任务执行(使用lambda表达式)");
System.out.println(Thread.currentThread().getName());
}, "lambda").start();
}
}
8、序列化 反序列化
示例:
package cn.learnj.ficca.advanced.seriable;
import java.io.Serializable;
public class Student implements Serializable {
private String name;
private String pwd;
public Student() {
}
public Student(String name, String pwd) {
this.name = name;
this.pwd = pwd;
}
public String getName() {
return name;
}
public String getPwd() {
return pwd;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
}
package cn.learnj.ficca.advanced.seriable;
import java.io.*;
import java.util.ArrayList;
public class SerTest {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//创建学生对象
Student student = new Student("赵", "z");
Student student1 = new Student("鞠", "j");
Student student2 = new Student("杨", "y");
ArrayList<Student> arrayList = new ArrayList<>();
arrayList.add(student);
arrayList.add(student1);
arrayList.add(student2);
//序列化操作
serializ(arrayList);
//反序列化操作
//rserializ();
}
private static void serializ(ArrayList<Student> arrayList) throws IOException {
//创建序列化流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("list.txt"));;
//写出对象
oos.writeObject(arrayList);
//释放资源
oos.close();
}
//反序列化
private static void rserializ() throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("list.txt"));
//读取对象,强转为ArrayList类型
ArrayList<Student> list = (ArrayList<Student>) ois.readObject();
for (int i = 0; i < list.size(); i++) {
Student s = list.get(i);
System.out.println(s);
}
}
}
9、反射
9.1 获取Class对象的方式:
- Class.forName(“全类名”):将字节码文件加载进内存,返回Class对象
- 多用于配置文件,将类名定义在配置文件中。读取文件,加载类
- 类名.class:通过类名的属性class获取
- 多用于参数的传递
- 对象.getClass():getClass()方法在Object类中定义着。
- 多用于对象的获取字节码的方式
- tips:
同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个。
9.2 Class对象的功能
9.2.1 获取成员变量
- Field[] getFields() :获取所有public修饰的成员变量
- Field getField(String name) 获取指定名称的 public修饰的成员变量
- Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符
- Field getDeclaredField(String name)
- Field:成员变量
- 相关操作:
设置值
- void set(Object obj, Object value)
获取值
- get(Object obj)
忽略访问权限修饰符的安全检查
- setAccessible(true):暴力反射
package cn.learnj.ficca.advanced.reflection;
public class Person {
private String name;
private int age;
public int a;
protected int b;
int c;
private int d;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getA() {
return a;
}
public void setA(int a) {
this.a = a;
}
public int getB() {
return b;
}
public void setB(int b) {
this.b = b;
}
public int getC() {
return c;
}
public void setC(int c) {
this.c = c;
}
public int getD() {
return d;
}
public void setD(int d) {
this.d = d;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", a=" + a +
", b=" + b +
", c=" + c +
", d=" + d +
'}';
}
public void work(){
System.out.println("我愿汗水流尽: ");
}
public void work(String why){
System.out.println("我愿汗水流尽: " + why);
}
}
package cn.learnj.ficca.advanced.reflection;
import java.lang.reflect.Field;
public class ReflectDemo {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
//0.获取Person类的Class对象
Class<Person> personClass = Person.class;
/*
1. 获取成员变量们
* Field[] getFields() :获取所有public修饰的成员变量
* Field getField(String name) 获取指定名称的 public修饰的成员变量
* Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符
* Field getDeclaredField(String name)
*/
//1. Field[] getFields() :获取所有public修饰的成员变量
Field[] fields = personClass.getFields();
for (Field field : fields) {
System.out.println(field);
}
System.out.println("-------------");
//2.Field getField(String name)
Field a = personClass.getField("a");
//获取成员变量a的值
Person person = new Person();
Object value = a.get(person);
System.out.println(value);
//设置a的值
a.set(person,10);
System.out.println(person);
System.out.println("---------");
//Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符
Field[] declaredFields = personClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
//在本例中,d是private类型,本不能在类外访问,在此可以使用暴力反射去访问d
//忽略访问权限修饰符的安全检查
Field d = personClass.getDeclaredField("d");
d.setAccessible(true);//暴力反射
Object value2 = d.get(person);
System.out.println(value2);
}
}
9.2.2 获取构造方法
- Constructor<?>[] getConstructors()
- Constructor getConstructor(类<?>… parameterTypes) : 参数类型(比如String.class)
- Constructor getDeclaredConstructor(类<?>… parameterTypes)
- Constructor<?>[] getDeclaredConstructors()
获取构造方法是为了创建对象
- Constructor:构造方法
- 创建对象:
T newInstance(Object… initargs)
如果使用空参数构造方法创建对象,操作可以简化:Class对象的newInstance方法, 如
Person p = personClass.newInstance()
获得一个Person对象
package cn.learnj.ficca.advanced.reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class ReflectDemo2 {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException {
//0.获取Person类的Class对象
Class<Person> personClass = Person.class;
/*
1. 获取构造方法们(为了创建对象)
* Constructor<?>[] getConstructors()
* Constructor<T> getConstructor(类<?>... parameterTypes)
* Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)
* Constructor<?>[] getDeclaredConstructors()
*/
//Constructor<T> getConstructor(类<?>... parameterTypes)
Constructor<Person> constructor = personClass.getConstructor(String.class, int.class);
System.out.println(constructor);
//创建对象
Person person = constructor.newInstance("赵", 23);
System.out.println(person);
System.out.println("-----------");
//使用空参构造
Constructor<Person> constructor1 = personClass.getConstructor();
System.out.println(constructor1);
//创建对象
Person person1 = constructor1.newInstance();
System.out.println(person1);
System.out.println("--------------");
//空参构造一般使用这种方式
Person person2 = personClass.newInstance();
System.out.println(person2);
}
}
9.2.3 获取成员方法
- Method[] getMethods()
- Method getMethod(String name, 类<?>… parameterTypes)
- Method[] getDeclaredMethods()
- Method getDeclaredMethod(String name, 类<?>… parameterTypes)
获取成员方法是为了执行方法
- Method:方法对象
执行方法:
- Object invoke(Object obj, Object… args)
获取方法名称:
- String getName:获取方法名
package cn.learnj.ficca.advanced.reflection;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectDemo3 {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
//0.获取Person类的Class对象
Class<Person> personClass = Person.class;
/*
1. 获取成员变量(是为了执行方法)
* Method[] getMethods()
* Method getMethod(String name, 类<?>... parameterTypes)
* Method[] getDeclaredMethods()
* Method getDeclaredMethod(String name, 类<?>... parameterTypes)
*/
//1. Method getMethod(String name, 类<?>... parameterTypes) 获取指定名称的方法
//空参
Method work_method = personClass.getMethod("work");
Person p = new Person();
//执行方法
work_method.invoke(p);
System.out.println("-----------");
//有参
Method work_method2 = personClass.getMethod("work", String.class);
//执行方法
work_method2.invoke(p, "不留遗憾");
System.out.println("---------");
//2. Method[] getMethods() 获取所有public修饰的方法
Method[] methods = personClass.getMethods();
for (Method method : methods) {
System.out.println(method);
System.out.println(method.getName());//获取方法名
// method.setAccessible(true);
}
}
}
9.2.4 获取类名
//获取类名
String name = personClass.getName();
System.out.println(name);
9.3 反射案例
需求:写一个"框架",不能改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法
实现:
1. 配置文件
2. 反射
步骤:
1. 将需要创建的对象的全类名和需要执行的方法定义在配置文件中
2. 在程序中加载读取配置文件
3. 使用反射技术来加载类文件进内存
4. 创建对象
5. 执行方法
package cn.learnj.ficca.advanced.reflectpro;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;
/**
* @program: basic-code
* @description: This is a program of reflection.
* @author: Mr.Luo
* @create: 2020-01-29 18:05
*/
//同样使用Person类
public class ReflectTest {
public static void main(String[] args) throws Exception {
//1.加载配置文件
//1.1 创建Properties对象
Properties properties = new Properties();
//1.2 加载配置文件,转换为一个集合
//1.2.1 获取class目录下的配置文件
ClassLoader classLoader = ReflectTest.class.getClassLoader();
InputStream resourceAsStream = classLoader.getResourceAsStream("pro.properties");
properties.load(resourceAsStream);
//2. 获取配置文件中定义的数据
String className = properties.getProperty("className");
String methodName = properties.getProperty("methodName");
//3. 加载该类进内存
Class cls = Class.forName(className);
//4. 创建对象
Object obj = cls.newInstance();
//5. 获取方法对象
Method method = cls.getMethod(methodName);
//6. 执行方法
method.invoke(obj);
}
}
9.4 操作获取Class对象
- 获取Class对象的方式:
- Class.forName(“全类名”):将字节码文件加载进内存,返回Class对象
- 多用于配置文件,将类名定义在配置文件中。读取文件,加载类
- 类名.class:通过类名的属性class获取
- 多用于参数的传递
- 对象.getClass():getClass()方法在Object类中定义着。
- 多用于对象的获取字节码的方式
package cn.learnj.ficca.advanced.reflectpro;
/**
* @program: basic-code
* @description: Threes ways of getting the object of Class
* @author: Mr.Luo
* @create: 2020-01-29 21:13
*/
public class ReflectDemo1 {
/*
* 获取Class对象的方式:
1. Class.forName("全类名"):将字节码文件加载进内存,返回Class对象
2. 类名.class:通过类名的属性class获取
3. 对象.getClass():getClass()方法在Object类中定义着。
*/
public static void main(String[] args) throws ClassNotFoundException {
//1. Class.forName("全类名")
Class cls1 = Class.forName("cn.learnj.ficca.advanced.reflectpro.Person");
System.out.println(cls1);
//2. 类名.class
Class cls2 = Person.class;
System.out.println(cls2);
//3. 对象.getClass
Person p = new Person();
Class cls3 = p.getClass();
System.out.println(cls3); //三者均为 class cn.learnj.ficca.advanced.reflectpro.Person
//比较三个对象是否相同
System.out.println(cls1 == cls2);//true
System.out.println(cls1 == cls3);//true
//即同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个。
}
}
10、注解
概念:说明程序的。给计算机看的
注释:用文字描述程序的。给程序员看的
定义:注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
概念描述:
- JDK1.5之后的新特性
- 说明程序的
- 使用注解:@注解名称
- 作用分类:
①编写文档:通过代码里标识的注解生成文档【生成文档doc文档】
②代码分析:通过代码里标识的注解对代码进行分析【使用反射】
③编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查【Override】
- JDK中预定义的一些注解
- @Override :检测被该注解标注的方法是否是继承自父类(接口)的
- @Deprecated:该注解标注的内容,表示已过时
- @SuppressWarnings:压制警告
- 一般传递参数all @SuppressWarnings(“all”)
- 自定义注解
格式:
元注解
public @interface 注解名称{
属性列表; //本质是成员方法
}本质:注解本质上就是一个接口,该接口默认继承Annotation接口
- public interface MyAnno extends java.lang.annotation.Annotation {}
属性:接口中的抽象方法
* 要求:
1. 属性的返回值类型有下列取值
* 基本数据类型
* String
* 枚举
* 注解
* 以上类型的数组2. 定义了属性,在使用时需要给属性赋值 1. 如果定义属性时,使用default关键字给属性默认初始化值,则使用注解时,可以不进行属性的赋值。 2. 如果只有一个属性需要赋值,并且属性的名称是value,则value可以省略,直接定义值即可。 3. 数组赋值时,值使用{}包裹。如果数组中只有一个值,则{}可以省略
元注解:用于描述注解的注解
* @Target:描述注解能够作用的位置
* ElementType取值:- TYPE:可以作用于类上 * METHOD:可以作用于方法上 * FIELD:可以作用于成员变量上 * @Retention:描述注解被保留的阶段 * @Retention(RetentionPolicy.RUNTIME):当前被描述的注解,会保留到class字节码文件中,并被JVM读取到 * @Documented:描述注解是否被抽取到api文档中 * @Inherited:描述注解是否被子类继承