原文:
面向过程是一种自上而下的编程模式,它将问题拆分为一个个步骤,每个步骤用函数实现,依次调用即可
面向对象是一种将事务高度抽象化的编程模式,它也是将问题分解为一个个步骤,它会对每个步骤进行相应的抽象,形成一个个对象,通过不同对象之间的调用,组合解决问题
面向过程:占用资源低,执行速度相对快
面向对象:占用资源高,执行速度相对慢,但是面向对象可以使工程更加模块化,实现更低的耦合和更高的内聚
封装、继承、多态
就是将客观事物封装成抽象的类,可以只让可信的类或者对象进行访问,对不可信的隐藏。
继承是指一个类可以继承另一个类的所有功能,并在无需重新编写原来类的情况下对其进行扩展,这种派生方式体现了传递性,除了继承,还有一种体现传递性的方式叫做实现。
两者的区别:
● 继承:使用父类的所有属性和方法,无需对其额外编码
● 实现:只能使用接口定义的全局变量和方法的名称,并且实现类必须提供方法的实现
所谓多态就是指一个类实例的相同方法在不同情形有不同的表现形式,使具有不同内部结构的对象可以共享相同的外部接口。
● 有类继承或者接口实现
● 子类要重写父类的方法
● 父类的引用指向子类的对象
编译看左边,运行看右边
JDK:「Java Development Kit」
● Java 开发工具包,提供了 Java 的开发环境和运行环境,包含编译 Java 源文件的编译器 Javac,还有调试和分析的工具。
JRE:「Java Runtime Environment」
● Java 运行环境,包含 Java 虚拟机和一些基础类库
JVM:「Java Virtual Machine」
● Java 虚拟机,提供了执行字节码文件的能力
数值型
● 整型:byte、short、int、long
● 浮点型:float、double
字符型
● char
布尔型
● boolean
包装类
● Byte、Short、Integer、Long、Float、Double、Character、Boolean
equals
● 是 java.lang.Object 的方法,这个方法返回的是 == 的判断,只不过有的类把这个方法重写了,所以才会比较内容是否相等,比如:String
Object的equeals方法:
类是对象的抽象,对象是类的具体
类是对象的模板,对象是类的实例
用来测试一个对象是否为一个类的实例,这个实例可以为一个对象或者一个接口
编译器会实时检查对象能否转换为类的实例
● 能确定实例类型的话,如果不能转换则直接编译不通过
● 不能确定实例类型的话,具体看运行时
注意:如果判断基本数据类型是是否属于对应包装类的话,比如:1 instanceof Integer,结果肯定是编译不通过
● 隐式转换就是自动类型转换,就是把一个小类型的数据赋值给大类型的数据,装箱;
● 显式转换就是强制类型转换,把一个大类型的数据强制赋值给小类型的数据,拆箱;
● char 在 java 中也是比较特殊的类型,它的 int 值从 0 开始,一共有 2 的 16 次(65536)方个数据;
● char<int<long<float<double
● char 类型可以隐式转成 int 、double 类型,但是不能隐式转换成 String;
● char 类型转成 byte、short 类型的时候,需要强转。
将基本数据类型放入集合类
包装类型与基本类型比较大小
包装类型的运算
三目运算符的使用
函数参数与返回值
属性、方法、内部类、构造方法、代码块
由于计算机中保存的小数其实是十进制小数的近似值,并不是准确值,所以最好使用 BigDecimal 或者 Long(单位为分)来表示金额
● public:表明该成员变量或者方法是对所有类、对象都是可见的,所有类或者对象都可以直接访问
● private:表明该成员变量或者方法是私有的,只有类自身对其具有访问权限,除此之外其他类或者对象都没有访问权限,包括子类
● protected:表明该成员变量或者方法对类自身、一个包中的其他类、子类是可见的,其他包下的类不可访问
● default:类的成员不写访问修饰符时默认为 default,表明该成员变量或者方法只对类自身、一个包中的其他类是可见的,子类、其他包下的类不可访问
不正确,3.4 是双精度数,将双精度型(double)赋值给浮点型(float)属于向下转型 会造成精度损失,因此需要强制类型转换 float f = (float) 3.4; 或者写成 float f = 3.4F;。
● 单精度浮点数在计算机存储器中占用 4 个字节(32 bits),利用「浮点(浮动小数点)」的方法,可以表示一个范围很大的数值。
● 双精度浮点数(double)则使用 8 个字节(64 bits) 来存储一个浮点数。
String
● String 是只读字符串,它并不是基本数据类型,而是一个对象。从底层源码来看是一个 final 类型的字符数组,所引用的字符串不能被改变,一经定义,无法再增删改。
● 每次对 String 的操作都会生成新的 String 对象
● 每次 + 操作,会隐式在堆上 new 一个跟原字符串相同的 StringBuilder 对象,再调用 append 方法拼接 + 后面的字符。
StringBuffer、StringBuilder
● StringBuffer 与 StringBuilder 都继承了 AbstractStringBulder 类,而 AbtractStringBuilder 又实现了 CharSequence 接口,两个类都是用来进行字符串操作的。
● 在做字符串拼接修改删除替换时,效率比 String 更高。
● StringBuffer 是线程安全的,Stringbuilder 是非线程安全的。
● 因为 StringBuffer 的方法大多都加了 synchronized 关键字,所以 Stringbuilder 比 Stringbuffer效率更高
● i++:先赋值,后计算
● ++i:先计算,后赋值
● 静态实例化:创建数组的同时指定数组中的元素
● 动态实例化:实例化数组的时候,只指定数组长度,数组中所有元素都是数组类型的默认值
不能,数组一旦实例化,它的长度就是固定的
创建一个新数组,从后到前循环遍历每个元素,将取出的元素按顺序放入新数组中
或者,使用一个临时变量,首尾两两交换
● byte、short、int、long 的默认值都是 0
● boolean 的默认值是 false
● char 的默认值是 \u0000
● float、double 的默认值是 0.0
● 对象类型的默认值是 null
● Java.lang
● Java.io
● Java.sql
● Java.util
● Java.awt
● Java.net
● Java.math
juc
● equals(Object obj)
● hashCode()
● toString()
● wait()
● notify()
● clone()
● getClass()
有,但是隐藏了,开发人员无法直接操作指针,由 jvm 来操作指针
在 Java 中,其实是通过「值传递」实现的参数传递,只不过对于 Java 对象的传递,传递的内容是对象的引用。
为了让大家都能理解你说的,你就说 Java 中只有值传递,只不过传递的内容是对象的引用。这也是没毛病的。但是,绝对不能认为 Java 中有引用传递。
编程语言中需要进行方法间的参数传递,这个传递的策略叫做求值策略。
在程序设计中,求值策略有很多种,比较常见的就是「值传递」和「引用传递」。还有一种值传递的特例是「共享对象传递」。
「值传递」和「引用传递」最大的区别是传递的过程中有没有复制出一个副本来,如果是传递副本,那就是值传递,否则就是引用传递。
形式参数:是在定义函数名和函数体的时候使用的参数,目的是用来接收调用该函数时传入的参数。
实际参数:在调用有参函数时,主调函数和被调函数之间有数据传递关系。在主调函数中调用一个函数时,函数名后面括号中的参数称为「实际参数」
构造方法不能当成普通方法调用,只有在创建对象的时候它才会被系统调用
重载指的是就是函数或者方法有同样的名称,但是参数列表不相同的情况,这样的同名不同参数的函数或者方法之间,互相称之为重载函数或者方法。
重写指的是在 Java 的子类与父类中有两个名称、参数列表都相同的方法的情况。由于他们具有相同的方法签名,所以子类中的新方法将覆盖父类中原有的方法,这叫重写。
● 参数列表、返回类型必须与被重写方法的相同;
● 访问级别的限制性:不能比被重写方法的强,但可以比被重写方法的弱;
● 重写方法不能抛出新的检查异常,也不能抛出比被重写方法更广泛的检查异常,但是能够抛出更少或更有限的异常
● 不能重写被标识为 final 的方法;
● 如果不能继承一个方法(如private修饰的),则不能重写这个方法。
● 被重载的方法必须改变参数列表;
● 被重载的方法可以改变返回类型、访问修饰符、可以声明新的或更广的检查异常;
● 方法能够在同一个类中或者在一个子类中被重载。
不能重写,但是能重载
静态内部类
● 静态内部类相对于外部类是独立存在的,
● 静态内部类可以访问外部类所有的静态变量和方法,即使是 private 的也一样,
● 静态内部类无法直接访问外部类的实例变量、方法,如果要访问的话,必须要 new 一个外部类的对象,使用 new 出来的对象访问。
● 静态内部类和一般类一致,可以定义静态变量、方法,构造方法等。
● 其他的类要访问静态内部类的属性或者调用静态内部类的方法,需要使用「外部类.静态内部类」的方式
● Java 集合类 HashMap 内部就有一个静态内部类 Entry。Entry 是 HashMap 存放元素的抽象,HashMap 内部维护 Entry 数组用来存放元素,但是 Entry 对使用者是透明的。像这种和外部类关系密切,且不依赖外部类实例的,都可以使用静态内部类。
普通内部类
● 普通内部类作为外部类一个成员而存在,在普通内部类中可以直接访问外部类属性,调用外部类的方法。
● 如果外部类要访问内部类的属性或者调用内部类的方法,必须要创建一个内部类的对象,使用该对象访问属性或者调用方法。
● 如果其他的类要访问普通内部类的属性或者调用普通内部类的方法,必须要在外部类中创建一个普通内部类的对象作为一个属性,才可以通过该属性调用普通内部类的方法或者访问普通内部类的属性
static 可以修饰内部类、方法、变量、代码块
● static 修饰的类是「静态内部类」
● static 修饰的方法是「静态方法」,表示该方法属于当前类,而不是属于某个对象,静态方法不能被重写
,可以直接使用类名来调用。在 静态方法中不能使用 this 或者 super 关键字。
● static 修饰的变量是「静态变量」或者叫「类变量」,静态变量被所有实例所共享,不会依赖于对象。静态变量在内存中只有一份拷贝,在 JVM 加载类的时候,只为静态变量分配一次内存。
● static 修饰的代码块叫「静态代码块」,通常用来做程序优化。静态代码块中的代码在整个类加载的时候只会执行一次。静态代码块可以有多个,如果有多个,按照先后顺序依次执行。
● static 还可以静态导包,这是 jdk 1.5 之后的新特性,举例:import static java.lang.Math.*;,假如要使用 Math 这个类里面的 sin() 方法,此时就不需要用 Math.,可以直接调用 sin();
● pow():幂运算(最终调用native方法)
● sqrt():平方根
● round():四舍五入
● abs():求绝对值
● random():生成一个 0-1 的随机数,包括 0 不包括 1
min()
max()
● charAt():返回指定索引处的字符
● indexOf():返回指定字符的索引
● replace():字符串替换
● trim():去除字符串两端空白
● split():分割字符串,返回一个分割后的字符串数组
● getBytes():返回字符串的byte类型数组
● length():返回字符串长度
● toLowerCase():将字符串转成小写字母
● toUpperCase():将字符串转成大写字符
● substring():截取字符串
● format():格式化字符串
● equals():字符串比较
toCharArray():转化为字符数组
intern():如果字符常量池有该字符串则直接返回,否则放入常量池并返回
只能继承一个类,但是可以实现多个接口
● super 表示当前类的父类对象
● this 表示当前类的对象
● 普通类不能包含抽象方法,抽象类可以包含抽象方法
● 抽象类不能直接实例化,普通类可以直接实例化
接口就是某个事物对外提供的一些功能的声明,是一种特殊的 java 类,接口弥补了 java 单继承的缺点
● 接口中的变量只能是 public static final 修饰的常量
● 接口中所有方法都是抽象方法
● 接口没有构造方法
● 接口不能直接实例化
● 一个类可以实现多个接口
抽象类
接口
Java 中有两类集合,List 和 Set,List 有序可重复,Set 无序不可重复,当我们要往 Set 中插入数据的时候,它是怎么判断要插入的数据是否重复呢,可以使用 equals() 方法,但是如果元素太多,执行效率就会比较慢。
于是有人发明了哈希算法来提高集合中查找元素的效率。这种方式会将集合分成若干个存储区域,每个对象可以计算出一个哈希码,可以将哈希码分组,每组分别对应某个存储区域,根据一个对象的哈希码就可以确定该对象应该存储的那个区域。
这样一来,当集合要添加新的元素时,先调用这个元素的 hashCode()方法,一下子就能定位到它应该存放的物理位置上。如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;
、有没有可能两个不相等的对象有相同的 hashCode
有可能,在产生 hash 冲突时,两个不相等的对象就会有相同的 hashcode 值
一般有以下几种方式来处理:
强引用
● 是平常中使用最多的引用,强引用在程序内存不足「OOM」的时候也不会被回收
软引用
● 软引用在程序内存不足时,会被回收,
● 可用场景: 创建缓存的时候,创建的对象放进缓存中,当内存不足时,JVM 就会回收早先创建的对象。
弱引用
● 弱引用就是只要 JVM 垃圾回收器发现了它,就会将之回收,
● 可用场景:Java 源码中的 java.util.WeakHashMap 中的 key 就是使用弱引用,
● 我的理解:一旦我不需要某个引用,JVM 会自动帮我处理它,这样我就不需要做其它操作。
虚引用
● 虚引用的回收机制跟弱引用差不多,但是它被回收之前,会被放入 ReferenceQueue 中。其它引用都是被 JVM 回收后才被传入 ReferenceQueue 中的。由于这个机制,虚引用大多被用于引用销毁前的处理工作。
● 虚引用创建的时候,必须带有 ReferenceQueue
● 可用场景: 对象销毁前的一些操作,比如说资源释放(Object.finalize() 虽然也可以做这类动作,但是这个方式不安全并且效率还低)
上面所说的这几类引用,都是指对象本身的引用,而不是指 Reference 的四个子类的引用
浅拷贝
对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝
深拷贝
对基本数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容
+= 操作符会进行隐式自动类型转换,此处 a += b 隐式的将加操作的结果类型强制转换为持有结果的类型,而a = a + b 则不会自动进行类型转换
有错,short 类型在进行运算时会自动提升为 int 类型,也就是说 s1 + 1 的运算结果是 int 类型,而 s1 是 short,此时编译器会报错
解决:将 s1 = s1 + 1; 改成 s1 += 1; 即可
例如,在Idea中:
性质不同
作用
使用 PreparedStatement 类,而不是使用 Statement 类
使用 CallableStatement
数据库连接是非常消耗资源的,影响到程序的性能指标。
连接池是用来分配、管理、释放数据库连接的,可以使应用程序重复使用同一个数据库连接,而不是每次都创建一个新的数据库连接。通过释放空闲时间较长的数据库连接,避免数据库因为创建太多的连接而造成的连接遗漏问题,提高了程序性能。
Druid、Dbcp、c3p0等,用的最多还是 Druid、c3p0,因为 Druid 更加稳定,安全;通过配置文件的形式来维护数据库信息,而不是通过硬编码。当连接的数据库信息发生改变时,不需要再更改程序代码就可以实现数据库信息的更新。
● & 是位运算符。
● && 是布尔逻辑运算符,在进行逻辑判断时用
● & 处理的前面为 false,后面的内容仍需处理,
● && 处理的前面为 false,则不再处理后面的内容。
● 数组没有 length() 这个方法,有 length 的属性。
● String 有 length() 这个方法
2 << 3
char 类型可以存储一个中文汉字,因为 Java 中使用的编码是 Unicode(不选择任何特定的编码,直接使用字符在字符集中的编号,这是统一的唯一方法),一个 char 类型占 2 个字节(16 比特),所以放一个中文是没问题的。
在实际开发中 ,可能会存在某些对象已经不再使用了,但是依旧被某些地方所引用的对象,这些对象不会被 GC 回收 ,因此会导致内存泄露的发生 。
都不能。抽象方法需要子类重写,而静态方法是无法被重写的,因此二者是矛盾的。本地方法是由本地代码(如 C 代码)实现的方法,而抽象方法是没有实现的,也是矛盾的。synchronized 和方法的实现细节有关,抽象方法不涉及实现细节,因此也是相互矛盾的。
不可以,静态方法只能访问静态成员,因为非静态方法的调用要先创建对象,在调用静态方法时可能对象并没有被初始化。
以前使用的是,BeanUtils.copyProperties();,但后来了解到它是通过反射来获取对象并赋值,这样效率较低,然后就换成了 Mapstruct,它会在编译项目时,根据定义好的接口,自动创建一个接口的实现类,这个实现类里面会根据接口中方法的形参以及返回值,生成对应的实现方法
接口可以继承接口 , 而且支持多重继承 。
抽象类可以实现接口,抽象类可继承具体类,也可以继承抽象类
可以继承其他类或实现其他接口
如下代码运行结果是?
Integer i1 = new Integer(1);
Integer i2 = new Integer(1);
int i3 = 1;
System.out.println("=====");
System.out.println(i1 == i2);
System.out.println(i1 == i3);
Integer i4 = 2;
Integer i5 = 2;
int i6 = 2;
System.out.println("=====");
System.out.println(i4 == i5);
System.out.println(i4 == i6);
Integer i7 = 128;
Integer i8 = 128;
int i9 = 128;
System.out.println("=====");
System.out.println(i7 == i8);
System.out.println(i7 == i9);
结果: