面向对象是一种软件开发思想,它是对现实世界的一种抽象,面向对象会把相关的数据和方法组织为一个整体来看待。
相对的另一种开发思想就是面向过程的开发思想,面向过程是一种以过程为中心的编程思想,面向过程一般是按照顺序执行每个方法,而面向对象可以不严格按照顺序执行每个方法。
面向过程
优点:性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源。比如,单片机、嵌入式开发、Linux/Unix 等一般采用面向过程开发,性能是最重要的因素。
缺点:没有面向对象易维护、易复用、易扩展。
面向对象
优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护。
缺点:性能比面向过程低。
Java摒弃了C++中难以理解的多继承,指针,内存管理等概念,不需要手动管理对象的生命周期。
四点:封装、继承、多态、抽象。
1)封装
封装,给对象提供了隐藏内部特性和行为的能力。对象提供一些能被其他对象访问的方法来改变它内部的数据。
在 Java 当中,有 4 种修饰符:default、public、private 和 protected 。
注意:java的访问控制是停留在编译层的,也就是它不会在.class文件中留下任何的痕迹,只在编译的时候进行访问控制的检查。其实,通过反射的手段,是可以访问任何包下任何类中的成员,例如,访问类的私有成员也是可能的。
区别:
public:可以被所有其他类所访问
private:只能被自己访问和修改
protected:自身、子类及同一个包中类可以访问
default:同一包中的类可以访问,声明时没有加修饰符,认为是friendly。
每一种修饰符给其他的位于同一个包或者不同包下面对象赋予了不同的访问权限。
下面列出了使用封装的一些好处:
通过隐藏对象的属性来保护对象内部的状态。
提高了代码的可用性和可维护性,因为对象的行为可以被单独的改变或者是扩展。
禁止对象之间的不良交互提高模块化。
2)继承
继承,给对象提供了从基类获取字段和方法的能力。继承提供了代码的重用行,也可以在不修改类的情况下给现存的类添加新特性。
3)多态
多态,是编程语言给不同的底层数据类型做相同的接口展示的一种能力。一个多态类型上的操作,可以应用到其他类型的值上面。
4)抽象
抽象,是把想法从具体的实例中分离出来的步骤,因此,要根据他们的功能而不是实现细节来创建类。
Java 支持创建只暴漏接口而不包含方法实现的抽象的类。这种抽象技术的主要目的是把类的行为和实现细节分离开。
JDK称为Java开发包 JDK=Java运行环境JRE + Java核心类库+ Java工具
java.lang.*
提供利用 Java 编程语言进行程序设计的基础类。最重要的类是 Object(它是类层次结构的根)和 Class(它的实例表示正在运行的应用程序中的类)。
java.util.*
包含集合框架、遗留的 collection 类、事件模型、日期和时间设施、国际化和各种实用工具类(字符串标记生成器、随机数生成器和位数组、日期Date类、堆栈Stack类、向量Vector类等)。集合类、时间处理模式、日期时间工具等各类常用工具包
java.io.*
Java的核心库java.io提供了全面的IO接口。包括:文件读写、标准设备输出等。Java中IO是以流为基础进行输入输出的,所有数据被串行化写入输出流,或者从输入流读入。
java.net.*
并非所有系统都支持 IPv6 协议,而当 Java 网络连接堆栈尝试检测它并在可用时透明地使用它时,还可以利用系统属性禁用它。在 IPv6 不可用或被显式禁用的情况下,Inet6Address 对大多数网络连接操作都不再是有效参数。虽然可以保证在查找主机名时 java.net.InetAddress.getByName 之类的方法不返回 Inet6Address,但仍然可能通过传递字面值来创建此类对象。在此情况下,大多数方法在使用 Inet6Address 调用时都将抛出异常。
java.sql.*
提供使用 JavaTM 编程语言访问并处理存储在数据源(通常是一个关系数据库)中的数据的 API。此 API 包括一个框架,凭借此框架可以动态地安装不同驱动程序来访问不同数据源。
简单类型 | boolean | byte | char | short | Int | long | float | double | void |
---|---|---|---|---|---|---|---|---|---|
二进制位数 | 1 | 8 | 16 | 16 | 32 | 64 | 32 | 64 | – |
封装器类 | Boolean | Byte | Character | Short | Integer | Long | Float | Double | Void |
十六进制整型常量:以十六进制表示时,需以0x或0X开头,如0xff,0X9A。
八进制整型常量:八进制必须以0开头,如0123,034。
长整型:长整型必须以L作结尾,如9L,342L。
浮点数常量:由于小数常量的默认类型是double型,所以float类型的后面一定要加f(F)。同样带小数的变量默认为double类型。
如:float f;
f=1.3f;//必须声明f。
字符常量:字符型常量需用两个单引号括起来(注意字符串常量是用两个双引号括起来)。Java中的字符占两个字节。一些常用的转义字符:
①\r表示接受键盘输入,相当于按下了回车键;
②\n表示换行;
③\t表示制表符,相当于Table键;
④\b表示退格键,相当于Back Space键;
⑤’表示单引号;
⑥’'表示双引号;
⑦\表示一个斜杠\。
(byte,short,char)–int–long–float—double
int到float,long到float,long到double 是不会自动转换的,不然将会丢失精度
①下面的语句可以在Java中直接通过:
byte b;int i=b; long l=b; float f=b; double d=b;
②如果低级类型为char型,向高级类型(整型)转换时,会转换为对应ASCII码值,例如
char c=‘c’; int i=c;
System.out.println(“output:”+i);输出:output:99;
③对于byte,short,char三种类型而言,他们是平级的,因此不能相互自动转换,可以使用下述的强制类型转换。
short i=99 ; char c=(char)i; System.out.println(“output:”+c);输出:output:c;
而在各个包装类中,总有形为××Value()的方法,来得到其对应的简单类型数据。利用这种方法,也可以实现不同数值型变量间的转换,例如,对于一个双精度实型类,intValue()可以得到其对应的整型变量,而doubleValue()可以得到其对应的双精度实型变量。
①调用类的串转换方法:X.toString();
②自动转换:X+"";
③使用String的方法:String.valueOf(X);
抽象方法只包含一个方法名,而没有方法体,使用abstract修饰
抽象类必须使用abstract修饰符来修饰
抽象方法也必须使用abstract修饰符来修饰
抽象方法不能有方法体
抽象类不能被实例化,无法使用new关键字来调用抽象类的构造器创建抽象类的实例。
抽象类包含成员变量、方法(普通方法和抽象方法都可以)、初始化块、内部类(接口、枚举)
抽象类的构造器不能用于创建实例,主要是用于被其子类调用。
抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类
抽象关键字不可以和那些关键字共存?Private不行;Static 不行;Final 不行
接口特点:
接口支持多继承
接口是隐式抽象的,当声明一个接口的时候,不必使用abstract关键字
接口中每一个方法也是隐式抽象的,声明时同样不需要abstract关键字
接口中的方法都是公有的
编译时自动为接口里定义的方法添加public abstract修饰符
Java接口里的成员变量只能是public static final共同修饰的,并且必须赋初始值,可以不写public static final,编译的时候会自动添加
接口与抽象类的区别:
接口里面不可以实现方法体,抽象类可以实现方法体
接口可以多继承接口,抽象类不可以
接口需要被子类实现,抽象类是被子类继承(单一继承)
接口中只能有公有的方法和属性而且必须赋初始值,抽象类中可以有私有方法和属性
接口中不能存在静态方法,但是属性可以是final,抽象类中方法中可以有静态方法,属性也可以
在生成对象的过程中,会先初始化对象的成员变量,然后再执行构造器。也就是说类中的变量会在任何方法(包括构造器)调用之前得到初始化,即使变量散步于方法定义之间。
静态属性初始化 静态方法块初始化 普通属性初始化 普通方法初始化 构造方法初始化
静态早于普通 属性早于方法 最后是构造方法
父类的构造器调用以及初始化过程一定在子类的前面。由于Circle类的父类是Shape类,所以Shape类先进行初始化,即先初始化成员变量,然后再执行Shape类的构造器。接着才是对子类Circle进行初始化,最后执行Circle的构造器。
逻辑运算符(boolean数据)
&& 称为短路与运算,仅当两个数都为真(即true)时,结果才为真
|| 称为断路或运算,其中一个数为真(即true)时,结果为真
&称为逻辑与运算,仅当两个数都为真(即true)时,结果才为真
| 称为逻辑或运算,其中一个数为真(即true)时,结果为真
! 称为逻辑非运算,将原数取反
A | B | A&&B | A||B | !A |
---|---|---|---|---|
true | true | true | true | false |
true | false | false | true | false |
false | true | false | true | true |
false | false | false | false | true |
位运算符(整型和字符型)
~ “按位非” ~A 将A按比特位取反,规则:遇1则0,遇0则1
& “按位与” A&B 按A与B的比特位取与, 规则:相对位全1则1,否则为0
| “按位或” A|B 按A与B的比特位取或, 规则:相对位全0则0,否则为1
^ “按位异或” A^B 按A与B的比特位取异或, 规则:相对位相同为0,否则为1
A | B | ~A | A&B | A|B | A^B |
---|---|---|---|---|---|
1 | 1 | 0 | 1 | 1 | 0 |
1 | 0 | 0 | 0 | 1 | 1 |
0 | 1 | 1 | 0 | 1 | 1 |
0 | 0 | 1 | 0 | 0 | 0 |
final修饰类时,表明这个类不能被继承。final类中的成员变量可以根据需要设置为final,但是要注意final类中的所有成员方法都会被隐式地指定为final方法
final修饰方法时,表明这个方法不能被任何子类重写。
final修饰变量分为两种情况,一种是基本数据类型,表示数据类型的值不会改变,另一种是引用类型,表示初始化之后不能再指向另一个对象。
除了RuntimeException和其子类,以及error及其子类,其他的都是checkedException
非静态内部类需要与外部类产生联系
创建内部类的方式
OuterClass outerClass = new OuterClass();
OuterClass.InnerClass innerClass =outerClass.new InnerClass();
如果是静态内部类
OuterClass outerClass = new OuterClass();
OuterClass.InnerClass innerClass = new OuterClass.InnerClass();
通过.this来在内部类中引用其外围类
return OuterClass.this;
先介绍一下两种不同的克隆方法,浅克隆(ShallowClone)和深克隆(DeepClone)。
浅克隆和深克隆的主要区别在于是否支持引用类型的成员变量的复制。
一般步骤是:
被复制的类需要实现Clonenable接口(不实现的话在调用clone方法会抛出CloneNotSupportedException异常), 该接口为标记接口(不含任何方法)
覆盖clone()方法,访问修饰符设为public。方法中调用super.clone()方法得到需要的复制对象。(native为本地方法)
如果引用类型里面还包含很多引用类型,或者内层引用类型的类里面又包含引用类型,使用clone方法就会很麻烦。这时我们可以用序列化的方式来实现对象的深克隆。
序列化:对象的寿命通常随着生成该对象的程序的终止而终止,有时候需要把在内存中的各种对象的状态(也就是实例变量,不是方法)保存下来,并且可以在需要时再将对象恢复。虽然你可以用你自己的各种各样的方法来保存对象的状态,但是Java给你提供一种应该比你自己的好的保存对象状态的机制,那就是序列化。
static 和 tranient关键字标注的成员变量将不会参与序列化
泛型”字面意思就是广泛的类型,代码与它们能够操作的数据类型不再绑定在一起,同一套代码,可以用于多种数据类型,这样,不仅可以复用代码,降低耦合,同时,还可以提高代码的可读性和安全性。也可以避免不同类型强转带来异常。
我们知道,Java有Java编译器和Java虚拟机,编译器将Java源代码转换为.class文件,虚拟机加载并运行.class文件。对于泛型类,Java编译器会将泛型代码转换为普通的非泛型代码,就像上面的普通Pair类代码及其使用代码一样,将类型参数T擦除,替换为Object,插入必要的强制类型转换。Java虚拟机实际执行的时候,它是不知道泛型这回事的,它只知道普通的类及代码。
反射机制是指在程序运行的过程中,对于任何一个类,都能够知道它的所有属性和方法;对于任意一个对象,都能知道和调用它的任意属性和方法,这种动态获取信息以及动态调用对象方法的功能称为Java的反射机制。
为什么强调在运行期间呢 因为编译完的class文件会生成Class对象,而反射是针对Class对象的,所以只能在运行期间。
一共有三种获取Class对象的方法:
虽然I/O类有很多,但是最基本的还是四个抽象类InputStream OutputStream Reader Writer
InputStream
OutputStream
Reader
Writer
UTF-8 是 Unicode 的实现方式之一。
UTF-8 最大的一个特点,就是它是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。
UTF-8 的编码规则很简单,只有二条:
1)对于单字节的符号,字节的第一位设为0
,后面7位为这个符号的 Unicode 码。因此对于英语字母,UTF-8 编码和 ASCII 码是相同的。
2)对于n
字节的符号(n > 1
),第一个字节的前n
位都设为1
,第n + 1
位设为0
,后面字节的前两位一律设为10
。剩下的没有提及的二进制位,全部为这个符号的 Unicode 码。
下表总结了编码规则,字母x
表示可用编码的位。
Unicode符号范围 | UTF-8编码方式 (十六进制) | (二进制) ----------------------+--------------------------------------------- 0000 0000-0000 007F | 0xxxxxxx 0000 0080-0000 07FF | 110xxxxx 10xxxxxx 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
跟据上表,解读 UTF-8 编码非常简单。如果一个字节的第一位是0
,则这个字节单独就是一个字符;如果第一位是1
,则连续有多少个1
,就表示当前字符占用多少个字节。
下面,还是以汉字严
为例,演示如何实现 UTF-8 编码。
TF-8编码方式
(十六进制) | (二进制)
----------------------±--------------------------------------------
0000 0000-0000 007F | 0xxxxxxx
0000 0080-0000 07FF | 110xxxxx 10xxxxxx
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
跟据上表,解读 UTF-8 编码非常简单。如果一个字节的第一位是0
,则这个字节单独就是一个字符;如果第一位是1
,则连续有多少个1
,就表示当前字符占用多少个字节。
下面,还是以汉字严
为例,演示如何实现 UTF-8 编码。
严
的 Unicode 是4E25
(100111000100101
),根据上表,可以发现4E25
处在第三行的范围内(0000 0800 - 0000 FFFF
),因此严
的 UTF-8 编码需要三个字节,即格式是1110xxxx 10xxxxxx 10xxxxxx
。然后,从严
的最后一个二进制位开始,依次从后向前填入格式中的x
,多出的位补0
。这样就得到了,严
的 UTF-8 编码是11100100 10111000 10100101
,转换成十六进制就是E4B8A5
。
retry:这retry就是一个标记,标记对一个循环方法的操作(continue和break)处理点,功能类似于goto,所以retry一般都是伴随着for循环出现,retry:标记的下一行就是for循环,在for循环里面调用continue(或者break)再紧接着retry标记时,就表示从这个地方开始执行continue(或者break)操作
使用continue跳出循环的操作:
public static void testContinue() {
retry:
for(int i = 0; i < 3; i++) {
for(int j = 0; j < 5; j++) {
System.out.print(j + ", ");
if(j == 3) {
continue retry;
}
}
}
System.out.print(" >>> OK");
}
// 输出:0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, >>> OK
使用breank跳出循环的操作:
public static void testBreak() {
retry:
for(int i = 0; i < 3; i++) {
for(int j = 0; j < 5; j++) {
System.out.print(j + ", ");
if(j == 3) {
break retry;
}
}
}
System.out.print(" >>> OK");
}
// 输出:0, 1, 2, 3, >>> OK