面向对象&面向过程
面向过程:把问题分解成一个一个步骤,每个步骤用函数实现,形成调用链,依次调用。
面向对象:把问题分解成步骤,然后对具体的步骤或者现实实体进行抽象,形成对象,通过对象之间的方法调用解决问题
面向对象的三大基本特征
封装:把现实世界中的事物抽象成一个具体的类,把事物的具体属性和方法封装在类中,具体的调用方不必知道具体的细节,只需知道调用什么方法、有什么功能。
继承:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。通过继承创建的新类称为“子类”或“派生类”,被继承的类称为“基类”、“父类”或“超类”。
多态:允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。
方法的重写和重载
重写:类继承某个类或者实现某个接口,重写父类或者接口的方法,然后具体使用时父类或者接口的引用指向具体的子类或者实现类。方法名和参数参数列表、返回值完全一样。运行时绑定。满足条件:子类继承父类或者接口实现&子类重写父类方法或者实现接口方法&父类的引用指向子类。直接代码指定引用的叫静态多态(直接指定实现类),通过spi 或者类型判断等等手段,获取具体的实现类,叫动态多态。
重载:同一个类中,方法名相同,参数列表不同,与返回值无关。编译期绑定,编译阶段就可以判断使用什么方法,降低运行时开销,代码更高效。
继承和实现区别
接口可以继承接口,抽象类可以实现接口,抽象类可以继承具体类。普通类可以实现接口,也可以继承抽象类和普通类。
继承只能单继承,一个类可以实现多个接口。
不使用多继承,是会产生菱形问题。比如说基类A,类B,C继承 A,类 D 又继承 B,C,这时候要是去调用 D 中的方法,刚好 B,C 中都有,D 又没有重写,就不知道调用谁的了。
接口和抽象类区别
接口只有抽象的方法,无具体实现。(java8后有具体的实现)
接口的访问修饰符必须是 public(默认),抽象类可以是 private\default\public\protected。
接口中的成员变量必须初始化。
抽象类可以有构造器,但是接口不能有。
接口可以被实现,接口之间可以继承,抽象类被继承。
一个类可以实现多个接口,但是只能继承一个类。
接口主要用于制定规范,抽象类主要是为了复用(模板模式)。
面向对象的五大基本原则
单一职责:一个类最好只做一件事情。这样可以提高代码的可维护性,减少修改代码对各功能的影响。
开放封闭:对修改封闭,对扩展开放。可以在不修改现有代码的情况下,扩展功能。比如说通过继承,不用修改原有的代码,重写方法,引用指向新实现类,这样新的功能使用使用新实现类,老的功能不受影响。
里氏替换:子类可以扩展父类的功能,但不能改变父类原有的功能。子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法。
依赖倒置:要面向接口编程,不要面向实现编程。
接口隔离:使用多而小的接口,不要使用大而全。
基本类型&包装类
基本类型(8种)
byte:
byte 数据类型是8位、有符号的,以二进制补码表示的整数;
最小值是 -128(-2^7);
最大值是 127(2^7-1);
默认值是 0;
byte 类型用在大型数组中节约空间,主要代替整数,因为 byte 变量占用的空间只有 int 类型的四分之一;
例子:byte a = 100,byte b = -50。
short:
short 数据类型是 16 位、有符号的以二进制补码表示的整数
最小值是 -32768(-2^15);
最大值是 32767(2^15 - 1);
Short 数据类型也可以像 byte 那样节省空间。一个short变量是int型变量所占空间的二分之一;
默认值是 0;
例子:short s = 1000,short r = -20000。
int:
int 数据类型是32位、有符号的以二进制补码表示的整数;
最小值是 -2,147,483,648(-2^31);
最大值是 2,147,483,647(2^31 - 1);
一般地整型变量默认为 int 类型;
默认值是 0 ;
例子:int a = 100000, int b = -200000。
long:
long 数据类型是 64 位、有符号的以二进制补码表示的整数;
最小值是 -9,223,372,036,854,775,808(-2^63);
最大值是 9,223,372,036,854,775,807(2^63 -1);
这种类型主要使用在需要比较大整数的系统上;
默认值是 0L;
例子: long a = 100000L,long b = -200000L。
"L"理论上不分大小写,但是若写成"l"容易与数字"1"混淆,不容易分辩。所以最好大写。
float:
float 数据类型是单精度、32位、符合IEEE 754标准的浮点数;
float 在储存大型浮点数组的时候可节省内存空间;
默认值是 0.0f;
浮点数不能用来表示精确的值,如货币;
例子:float f1 = 234.5f。
double:
double 数据类型是双精度、64 位、符合 IEEE 754 标准的浮点数;
浮点数的默认类型为 double 类型;
double类型同样不能表示精确的值,如货币;
默认值是 0.0d;
例子:boolean one = true。
char:
char 类型是一个单一的 16 位 Unicode 字符;
最小值是 \u0000(十进制等效值为 0);
最大值是 \uffff(即为 65535);
char 数据类型可以储存任何字符;
例子:char letter = 'A';。
有了基本数据类型还要有包装类
因为 java 是面向对象的语言。比如说说集合类的操作都是泛型,必须是 Object,int 这些是放不进去的。
基本类型和包装类的区别
默认值不同,包装类都是 null。
初始化方式不同,包装类要 new。
存储方式不同,基本类型是保存在栈上,包装类型保存在堆上。(JIT优化可能分配在栈上)
rpc 接口应该使用在包装类,避免 Null和0的区别。
拆装箱
拆箱就是指把包装类转化为基本类型,比如 new Integer(10).intValue()。
装箱就是指把基本数据类型转化为包装类,比如 Integer.valueOf(10)。其中-128到127是有缓存的,可以通过-XX:AutoBoxCacheMax=Size 来进行修改。
金额
不能使用 float 和 double
因为10进制转2进制。整数是除以2取余,逆序排列,小数是乘以2取整,顺序排列。
不是所有的10进制都能转换成2进制的整数,比如0.1就二进制就是无限循环下去,这样精度就会丢失。
应该使用 BigDecimal。
BigDecimal 使用方式
用 BigDecimal.valueOf("0.1")。
不要用 equals 比较,equals 是会比较对象的属性的,比如精度。
比较用 compareTo()。
String&StringBuilder&StringBuffer
String
不可变,内部方法都会生成新的 String 对象,字符串太常用了,保证线程安全。
"+"号拼接,反编译过来其实就是 StringBuilder 拼接。
String a=new String("abc"); 创建1个或2个对象。
"abc"这是字面量,如果不在字符串常量池中,会先在字符串常量池中创建。如果在字符串常量池中,则返回常量池中的引用。
字面量有可能一开始就在 class 常量池中,并不会立刻解析成对象,然后第一次调用的时候才会在字符串常量池中创建。
String.intern()方法:
如果字符串常量池中有这个字符串,则返回对象的引用。
如果没有,则会在字符床常量池中先创建,然后返回引用。
使用的好的话,可以避免大量字符串的创建。
使用时要注意 JDK 版本这些都是字符串维护,会被提前加入到字符串常量池中。
String 的长度有限制,如果是编译期,长度是65535,运行期是 Integer.MAX_VALUE。
编译期是用2位 byte 来存储长度,故2的8次方-1。
运行期长度是 int,故 Integer.MAX_VALUE。
StringBuilder
线程不安全,可以拼接字符串。由于 JIT 优化,一般情况下,会栈上分配。
StringBuffer
线程安全,效率低,底层是通过synchronized来实现的。
泛型
泛型的实现是通过类型擦除来实现的,也就是你写好的 java 类,编译成.class 文件的时候就已经被编译成具体的类型了。JVM 是不知道什么是泛型的,都是具体的类型。
java 很多实现都是这样,减少运行时的损耗。
评论区