编程语言:java
不定期更新,记录自己学习的java小知识。
Java放置在堆中的变量有默认值,如对象数据,类的属性等;而栈中的(局部变量),方法区中的(static属性等)变量必须要有初始值,无默认值。
Java 方法,属性,构造器,内部类的权限修饰符:public,缺省,protected,public;类修饰符:public,缺省
用Arrays的copyOf方法拷贝原数组内容,并设置新的长度。ArrayList扩容需要做一次数组拷贝,如果是反复扩容,肯定会对程序的运行效率产生影响。所以在初始化ArrayList的时候,尽量设置初始化容量,避免其扩容。
java继承时,父类中的private属性和方法都会被继承,但是因为封装的原因不可使用对象调用。即 类内this. 与类外对象object. 方式调用都不行。
Java重写(覆盖)方法的时候,子类方法权限不小于父类方法权限。
java属性不会出现覆盖,如果子类父类中出现同名的属性,则实际上有了两个属性,需要用this和super区分。默认调用子类自己的。
java多态,编译看左边父类,运行看右边子类。对于属性,只能使用父类属性。对于方法,只能使用父类中定义的方法,但是执行的是子类中重写的方法,这被称为虚拟方法调用。多态是运行时行为,只有在执行时才知道调用的是哪种子类的重写方法。
object类中equals()方法与“==”运算符功能相同,因此不能用来比较两个对象的内容是否相等。String,Date,file,包装类等中的equals()方法进行了重写,用来比较两个字符串内容是否相同。因此若要比较两个对象是否相等,需要重写equals方法。一般可以用IDE自动生成。
java中的内存地址都是虚拟的内存地址,是hashcode计算得到的一个值。在操作系统之上,设置了一个JVM。
为每一种基本数据类型定义的相应的引用类型为包装类(封装类)。主要用于针对基本数据类型的面向对象化。可以对基本数据类型进行封装,并且增加新的功能。
包装类可以自动装箱,即Integer i = 10这种写法是可以的,而不需要new构造。同样的也可以自动拆箱:int j = i 这种是可以的。
String.valueof(基本数据类型,包装类)得到String
包装类.parseXXX(String)得到基本数据类型,如Integer.parseInt(String)。三元运算符会提升数据类型,如下图所示:
Java中-128到127小数存储,因此包装类包装小整数,并且使用自动装箱方式,也会被认为是同一个对象,类似于python中的小整数池[-5,256]。实现方法都是内建类。
单例模式:
饿汉式:对象加载时间长(坏)线程安全(好)
懒汉式:延迟对象的创建(好)如果不能保证原子性操作(加锁)则线程不安全(不好)单例模式需要私有化构造器,如果使用私有的构造器,当你尝试去new的时候编译器就会马上阻止你当前的操作。
接口里面没有构造器,因此不能实例化。所以类必须实现接口中的方法。如果没有实现所有的抽象方法,则该类为抽象类。如果全部实现则可以实例化该类。
模板设计模式本质是抽象类的使用,在模板类中有模板方法,模板方法定义了程序执行的流程,调用了模板类中的其他方法,有些方法为抽象方法,这些方法被称为钩子方法,通过继承模板类的子类实现钩子方法,以实现不同的功能,体现出多态。
接口的继承问题:AA,BB中都有抽象方法method1,CC继承了AA和BB,class1实现了CC,对于method1因为都是抽象方法,都没有具体实现,因此并不会存在python多继承中父类同名方法的继承顺序问题,毕竟AA,BB接口中的两个method1都没有具体实现,接口只是增加功能。
接口也可以跟父类一样,做向上继承,如初始化或者在方法参数中声明一个接口,然后具体传入的是一个接口的实现。
package com.moon.java;
public class InterfaceTest3 {
public static void main(String[] args) {
Computer computer = new Computer();
computer.transferInfo(new Printer());
computer.transferInfo(new Flash());
}
}
class Computer{
public void transferInfo(USB usb) {
usb.start();
usb.stop();
}
}
interface USB{
public abstract void start();
public abstract void stop();
}
class Printer implements USB{
public void start() {
System.out.println("打印机开始工作");
}
public void stop() {
System.out.println("打印机结束工作");
}
}
class Flash implements USB{
public void start() {
System.out.println("U盘开始工作");
}
public void stop() {
System.out.println("U盘结束工作");
}
}如果子类或实现类继承的父类和实现的接口中声明了同名同参数的方法,则子类在没有重写方法的情况下,默认调用的是父类中的方法。(类优先原则)
实现类实现了多个接口,多个接口中定义了同名同参数的默认方法,则在实现类没有重写的情况下,则会报错,这种情况称为接口冲突,想要解决只能重写,或者定义不重名的接口默认方法。
如何调用接口中和父类中的同名方法与属性、常量:一般来说接口中的全局常量命名全大写,因此符合命名规范的情况下,不会引起冲突。
MyThread继承Thread类实现多线程,重写run方法(用来实现自己的程序逻辑),MyThread实例化对象myThread后调用start()方法,该方法创建新的线程,并且调用run()方法。不能直接调用run()方法,因为这意味着没有创建新的线程,因此也就不是多线程编程了。
一个myThread实例化多线程对象只能调用start()方法一次,如果想再开一个线程,则需要新建一个多线程对象。线程优先级高不代表线程一定先执行,只是概率上调度优先被调度。
多个线程如果有共享数据,则使用runnable接口方式天然可以保证一个runnable方式被多个thread共享。并且可以解决类的单继承局限性。
任何对象可以充当锁,要求每个线程必须共用同一把锁。
package com.moon.exer;
/**
*
* @Description 线程不安全性的例子,任何对象都可以充当锁,但是每个线程必须共用一把 优点:解决线程不安全 缺点:同步的代码或者方法,实际上为单线程。
*
*
*/
public class ThreadSecureDemo {
public static void main(String[] args) {
Count count = new Count();
Thread t1 = new Thread(count);
Thread t2 = new Thread(count);
t1.start();
t2.start();
}
}
class Count implements Runnable {
private int count = 0;
Object mutex = new Object();
public void run() {
// synchronized (this) { 这种写法也行,此时不需要新建一把锁。
// 因为window w1对象是唯一的。不管何种写法只需要保证锁唯一即可。
synchronized (mutex) {
for (int i = 0; i < 10000000; i++) {
count++;
}
System.out.println(count);
}
}
}同步代码块方式同步:类也是对象,并且只会加载一次,因此是唯一的,可以作为线程同步的锁。
为何反射机制中,getDeclaredMethod(name, Class… parameterTypes) 需要填写参数类型?因为系统通过函数名,参数类型与参数类型顺序唯一确定一个函数,如果有函数重载发生,则可以通过参数类型区分。
字符串存于方法区常量池中,JVM1.7之前在永久代,1.7在堆中,1.7后在元空间(永久代被取消,成为元空间)
链式调用方法链的原理:一个对象的方法返回值为对象本身即可。object.method()返回object,则可以继续后面跟着.method()
sleep()和wait()都会阻塞,但是wait()会释放锁。wait()和notify()/notifyAll()用于线程间通信,必须在同步方法和代码块中。notify()唤醒一个优先级高的线程,notifyAll()唤醒所有。
wait()和notify()/notifyAll()的调用者必须是所在同步方法和代码块对应的锁。因为任何对象都可以当锁,因此这三种方法定义在了Object类中。
子程序throw 异常,主程序try/catch子程序的异常。如果子程序之间有顺序执行关系,若第一个子程序内部try/catch,则在处理完子程序异常之后,主程序仍然不受影响的继续执行第二个子程序,这样是没有意义的。因此所有的异常由主程序判断如何处理。
set添加元素时,保证添加的元素按照equals()方法判断,不能返回true。即相同元素只能添加一个。
set删除元素时,remove方法根据hashcode进行删除,如果hashcode找得到则使用equal()方法判断是不是要删除的数据,如果集合内的元素发生了修改,则无法再次根据原来的引用进行删除。