1.封装
封装是面向对象的三大特征之一,意思为隐藏对象的属性和实现细节,Java中的封装就是根据访问控制修饰符实现的
- public(公共的):对外公开,所有类都可以访问
- protected(受保护的): 只能在同一个包中的类或子类访问
- default(默认级别): 只有在同一个包中类能访问
- private(私有的): 不对外公开,只能在该类的对象内部访问
使用private修饰属性,在通过相应的get和set方法来暴露属性达到封装的效果,使用该方法的好处
1.灵活控制读和写的访问级别,有些属性只准读不准修改就可以公开读方法,封装写方法
2.防止错误的修改属性,当属性password有6位数的限制,通过set方法可以很容易的实现该限制
2.多态是怎么实现的
都知道多态是一个父类下有多个子类并且这些子类都重写了该父类的某个方法,这时候声明父类类型但指向不同的子类时,调用父类被重写的方法,实际运行的确实子类的方法,Java虚拟机称这种机制为动态绑定,我所理解的是声明的是父类的类型,所以只能使用父类的方法和属性,但是使用到重写的方法时,虚拟机会自动绑定到指向的子类方法进行执行。
3.Java源文件关于public修饰class的限制
每个Java源文件中可包含多个类或接口的定义,但是最多只有一个类或接口是public修饰的,并且Java源文件必须以其中public修饰的类名命名
这种情况下他们都是一个单独的类只是被放到了一个Java源文件中,School的访问控制修饰符为default,new创建School对象时必须在同一个包下
4.基本类型自从转换规则
从小类型到大类型遵循
- byte->short->int->long->float->double
- char->int->long->float->double
但long自动转换成float会丢失精度
错误是因为a+1的1是int型直接数,byte+int应为int,那为什么1能赋值给byte,因为在赋值时在该类型的取值范围内允许直接赋值(赋值操作符,例如:=,+=,*=,-=,/=,%=),运算操作符就不行。
wait 的虚假唤醒
wait需要放在循环中检查条件,唤醒后还需要检查
public class MultipleConsumersProblem {private static final Object lock = new Object();private static int inventory = 1; // 只有1个商品public static void main(String[] args) throws InterruptedException {// 两个消费者for (int i = 1; i <= 2; i++) {final int consumerId = i;new Thread(() -> {synchronized (lock) {//使用 ifif (inventory <= 0) {try {System.out.println("消费者" + consumerId + ": 等待商品");lock.wait();System.out.println("消费者" + consumerId + ": 被唤醒");} catch (InterruptedException e) {e.printStackTrace();}}inventory--;System.out.println("消费者" + consumerId + ": 成功购买,库存: " + inventory);}}).start();}Thread.sleep(1000);// 生产者补货synchronized (lock) {inventory = 1; // 只补1个商品lock.notifyAll(); // 唤醒所有消费者}}
}
输出结果:
消费者1: 等待商品
消费者2: 等待商品
消费者1: 被唤醒
消费者1: 成功购买,库存: 0
消费者2: 被唤醒
❌ 消费者2: 成功购买,库存: -1
可以发现消费者2也购买成功了,但库存实际不够了,所以wait后应该还检查一次,将if修改成while就可以解决,当然在自减时在进行判断也是可以的但代码不简洁美观