本篇博文目录:
- 1.什么是JDK,JRE,JVM?
- 2.JAVA编译过程?
- 3.标识符(命名规范)
- 4.关键字
- 5.数据类型
- (1)定义
- (2)8种基本数据类型
- 6. 常量,变量(访问修饰符)
- (1) 常量
- (2) 变量
- (3)局部变量和全局变量
- (4)访问权限
- 7. 数据类型转换(自动类型转换,强制类型转换)
- (1).自动类型转换(隐式转换)
- (2).强制类型转换(显示转换)
- 8.运算符
- (1) 关于++和- -
- (2)关于&&,||与&,I
- (3)关于三目运算
- 9.表达式,顺序,选择(if,if else else if/switch),循环(while(){}; do{}while();for ;for增强)
- (1)表达式
- (2)选择
- (2)循环
- 10.数组
- (1)声明数组变量
- (2)创建数组
- (3)数组的访问
- 11.OOP编程
- (1) 方法
- (2).类
- (3).对象
- (4)三大特性(封装,继承,多态)
- 12.异常机制
- 13.集合(Set,List,Map)
- 14.IO流
- 15.多线程
- 16.网络编程(TCP【重点】;UDP)
1.什么是JDK,JRE,JVM?
- JDK:是程序员使用java语言编写java程序所需的开发工具包(包括JRE,工具等),是提供给程序员使用的。
- JRE:是用户运行Java程序需要的运行环境(java虚拟机,java基础类库)。
- JVM:JVM虚拟机,
用来解释执行字节码文件(class文件:通过Javac翻译.java得到),java的跨平台就是通过JVM来实现的。
2.JAVA编译过程?
JAVA编译过程:
Java编译程序(javac)将Java源程序(HelloWorld.java),翻译成Java字码文件(HelloWorld.class),JVM虚拟机对HelloWorld.class进行解释执行。
下面通过CMD的方式执行一个HelloWorld的demo来体验一下这个过程。
过程如下🔻:
- 用记事本创建一个HelloWorld.java的文件(—>另成为—>数据类型选为所有—>输入HelloWorld.java保存)
- 进入dos界面(Windows+R输入cmd),cd进入到桌面(HelloWorld.java在哪里就进入到哪里),输入Javac HelloWorld.java
- 此时对应的目录下就会生成一个HelloWorld.Class的文件
- 输入java HelloWorld
3.标识符(命名规范)
用来定义变量,方法,类等要素命名时使用的字符串序列。
规则
:
- 应以字母,下划线,$符号开头
- 应以字母,下划线,$符号或数字组成
- 对大小写敏感,长度无限制
- 不能使用关键字
非法标识名举例:
Class(关键字),9aa(不能以数字开头),Hee LL(不能出现空格)
4.关键字
有特殊含义的、被保留的、不能随意使用的字符(小写)。
5.数据类型
(1)定义
Java语言是一种强类型语言,所谓的强制类型指的是对于每一种数据都有明确的数据类型,每个数据类型占多少个字节(JavaScripct中的变量都是通过Var进行声明,没有明确指定数据类型,所以JS不是一种强制类型的语言.我们常称为弱类型语言)。
在Java中数据类型可分为二类,基本数据类型
和引用数据类型
(下面只介绍8种基本数据类型
,这8种数据类型也是最核心的,必须掌握的)。
(2)8种基本数据类型
- 整数(4种:byte,short,
int(默认)
,long;字节数分别为:1B,2B,4B,8B) - 浮点数(float,
double(默认)
;字节数分别为:4B,8B) - 字符(char;字节数为:2B)
- 布尔(boollean;字节数分别为:1B)
- 关于取值范围(除字符和布尔外):-2(n-1)~2n -1 n:位数
- 字符取值范围:0~2n -1 布尔:true和false
6. 常量,变量(访问修饰符)
(1) 常量
程序在执行过程中其值是不可以改变的量叫做常量
语法:
【访问权限】final 数据类型 常量名=初始值例子:
public final int COUNT=0;
注意事项
在定义常量时就需要对该常量进行初始化。
- final 关键字不仅可以用来修饰基本数据类型的常量,还可以用来修饰对象的引用或者方法。
- 为了与变量区别,常量取名一般都用大写字符。
常量的初始化,还可以放在类构造器中进行初始化(该常量未被static修饰):
二种初始化的方式只能使用其中的一种,不然不符合语法规则:
引用类型的常量,不可变指的是地址不可变,但是对象里的值是可变的:
测试类代码(引用类型的常量,不可变指的是地址不可变
):
测试类代码(但是对象里的值是可变的
)
运行效果:
(2) 变量
程序在执行过程中其值是可以改变的量叫做变量
语法:【访问权限】 数据类型 变量名【=初始值,可以初始化,也可以不用进行初始化】;
下图给出的是8种数据类型,所占存储空间,表示的范围和默认值:
注意:
- 局部变量必须进行初始化即赋值,才能使用(它没有默认值)
全局变量可以不进行初始化,使用对应数据类型的默认值(未规定数据类型的时候,整型默认:int,浮点默认:double)。
局部变量必须进行初始化:
全局变量可以不进行初始化,使用对应数据类型的默认值:
运行效果:
(3)局部变量和全局变量
局部和全局意思可理解为该变量可访问的范围,局部变量可访问为对应所在{}代码块的区域,而全局变量在整个Class的{}代码块下,意思就是在本类都可以进行访问(无需关注访问权限),并且全局变量可以被其他类(同包,非同包)进行访问,前提是访问权限足够(该全局变量足够开放)。
public class demo{
int a;//全局变量
public void start(){
int c;//局部变量
}
public void start2(int c){ //c 局部变量
}
}
(4)访问权限
主要用于其他类(本包,非本包,非本包子类,非本包非子类)对目标类进行访问时根据权限来看该类是否可以进行访问。
注意:
- default:表示不加任何访问修饰符(通常称为“默认访问权限“或者“包访问权限”)
7. 数据类型转换(自动类型转换,强制类型转换)
(1).自动类型转换(隐式转换)
当数据类型不一样时,将会发生数据类型转换。
-
特点:
代码不需要进行特殊处理,自动完成。 -
规则:
数据范围从小到大。
🔻(右面100是未规定数据类型,即该数据类型默认为int,左边为long,右边int数据类型的数据放入long(小->大)发生了自动类型转换。)
long num=100;
右面2.5F规定了数据类型为float,(如果不加F表示数据类型为double),左面数据类型为double(小->大)发送了自动类型转换。
double num2 = 2.5F;
由于浮点数默认使用double,在使用float的时候需要在右侧加上f,不然会报错:
备注:关于什么是小什么是大,其实指的就是该数据类型所占的字节数大小。
(2).强制类型转换(显示转换)
如果数据类型没有发生自动转换,也可以通过强制类型进行转换但是这样可能会导致数据丢失。
-
特点:
代码需要进行特殊的格式处理,不能自动完成。 -
格式:
范围小的类型 范围小的变量名= (范围小的类型)原本范围大的数据;
🔻(右面数据类型为long,左面数据类型为int(大->不发生自动类型转换),(int)表示该long数据类型强制转换为int。)
int num = (int) 100L ;
备注:
强制类型转换一般不推荐使用,因为有可能发生精度损失、数据溢出。
- byte/short/char这三种类型都可以发生数学运算,例如加法“+”
- byte/short/char这三种类型在运算的时候,都会被首先提升成为int类型,然后再计算。
这里并不是四舍五入,所有的小数位都会被舍弃掉:
运行效果:
🔻 int和浮点类型之间可以进行强制类型转换
运行效果:
🔻char可以通过强制类型转换为int,对应的值为字符的ASCLL的值
🔻(对于byte/short/char三种类型来说,如果右侧赋值的数值没有超过范围,那么java编译器将会自动隐含地补上一个(byte)(short)(char)。)
- 如果没有超过左侧的范围,编译器自动隐含地补上(byte)进行强转。
byte num1 = /*(byte)*/ 30; //右侧没有超过左侧的范围
System.out.println(num1); // 30
- 如果右侧超过了左侧范围,那么编译器报错。
8.运算符
运算符 用于连接 表达式 的 操作数,并对操作数执行运算。
运算符速查表:
运算符优先级:
(1) 关于++和- -
count++:表示先
执行
再运算
++count:表示先运算
再执行
备注:“--”
同理
//关于count++
int count=0;
System.out.println(count++);//0
System.out.println(count);//1
int count1=0;
count1++;
System.out.println(count1);//1
//关于++count
int count2=0;
System.out.println(++count2);//1
int count3=0;
++count3;
System.out.println(count3);//1
注意:++(i++)这种写法不存在!
(2)关于&&,||与&,I
(&&,||)表示如果前面一项表达式已经满足要求了后面一项的表达式就无需再进行相应的判断了。
如下:count=100已经满足表达式1(count>=100)对于表达式2(count<500)就不用进行判断,但是条件为“|”时,表达式1和表达式2都要进行运算(这就是短路与与短路或的作用)。
int count=100;
if (count>=100||count<500) {
System.out.println("执行");
}
(3)关于三目运算
三目运算表达式进行判断之后,表达式为true返回
“:”
左面的值,如果为false返回“:”
int count=100;
boolean result=(count==100?true:false);
System.out.println(result);//true
9.表达式,顺序,选择(if,if else else if/switch),循环(while(){}; do{}while();for ;for增强)
(1)表达式
表达式运算后的结果要么为真要么为假
语法:
变量/常量 比较运算 变量/常量 【逻辑运算】 变量/常量 比较运算 变量/常量
🔻(下面的表达式a>b,a<B可以分别看作一个小的表达式,整体可以看作是一个大的表达式)。
例子:a>b&&a<B
(2)选择
if(表达式){
//执行符合表达式要求的代码
}
if(表达式1){
//执行符合表达式1的代码
}else if(表达式2){
//执行符合表达式2的代码
}
if(表达式1){
//执行符合表达式1的代码
}else if(表达式2){
//执行符合表达式2的代码
}else{
//执行不满足所有不符合表达式1和表达式2的代码
}
🔻注意: switch中只有当遇到break的时候才退出(意思就是当switch满足情况1的情况下,执行情况1的代码,但是情况1结尾处如果没有break就继续执行情况2的代码直到遇到break才退出)。
switch(变量/常量){
case 变量/常量(同一数据类型):
//执行满足该情况的代码
break;
case 变量/常量(同一数据类型):
//执行满足该情况的代码
break;
default:
//执行不满足所有上面情况的代码
break;
}
在JDK1.8中,switch支持的参数如下图所示:
(2)循环
- break:结束本循环
- continue:结束本次循环
i的变化:i=0,1,2,3…19 当i=20的时候执行i<20不满足本次循环结束
for(int i=0;i<20;i++){
//执行20次 i的变化:i=0,1,2,3.....19 当i=20的时候执行i<20不满足本次循环结束
}
备注:for的执行顺序为i = 0,然后i<20,方法题{}中的代码,i++,再然后执行i<20,方法题{}中的代码,i++,后面按到此顺序循环直到i<20不满足条件为止。
for(集合泛型数据类型:集合)增强
ArrayList<String> arrayList=new ArrayList<>();
arrayList.add("1");
arrayList.add("2");
arrayList.add("3");
arrayList.add("4");
for(String aString:arrayList){
System.out.println(aString);//允许情况:1 2 3 4
}
备注:泛型类型指的是一组数据集合中共同采用的数据类型,是对一组数据类型的限定。
while(表达式)直到表达式为false才停止循环
while (表达式) {
//直到表达式为false停止循环
}
do{}while(表达式);先执行一次,然后当表达式为false时停止循环
do{
//先执行一次,然后当表达式为false时停止循环
}while(表达式);
10.数组
存放一组相同数据类型的数据。
(1)声明数组变量
首先必须声明数组变量,才能在程序中使用数组。下面是声明数组变量的语法:
- 数据类型[] 数组名; // 首选的方法
- 数据类型 数组名[]; // 效果相同,但不是首选方法
实例:
- double[] myList; // 首选的方法
- double myList[]; // 效果相同,但不是首选方法
(2)创建数组
下面的10表示数组的长度
myList= new double[10];
另外一种创建数组的方式:
数据类型[] 数组名= {value0, value1, ..., valuek};
(3)数组的访问
数组的元素是通过索引访问的。数组索引从 0 开始,所以索引值从 0 到 arrayRefVar.length-1。
🔻(表示访问数组myList索引为0的对应数据。)
myList[0];
🔻(myList.length:表示获取myList数组的长度)
myList.length;
实例:
public class TestArray {
public static void main(String[] args) {
// 数组大小
int size = 10;
// 定义数组
double[] myList = new double[size];
myList[0] = 5.6;
myList[1] = 4.5;
myList[2] = 3.3;
myList[3] = 13.2;
myList[4] = 4.0;
myList[5] = 34.33;
myList[6] = 34.0;
myList[7] = 45.45;
myList[8] = 99.993;
myList[9] = 11123;
// 计算所有元素的总和
double total = 0;
for (int i = 0; i < size; i++) {
total += myList[i];
}
System.out.println("总和为: " + total);
}
}
11.OOP编程
(1) 方法
避免代码臃肿,将某一个操作的代码移到定义的方法中,当用户需要执行该操作时,只需要调用该方法即可,程序就会进入到方法中去执行该操作的代码。
方法语法如下:
[访问权限] [static] [..] 返回值类型 方法名([形参1][形参2]..[形参n]){
//当方法被使用时执行代码块的代码
return 返回值类型;
}
例子:
运行效果:
关于方法的返回值类型为void:
表示没有返回值,不用写
return 返回值类型
数据类型就是基本数据类型或者引用数据类型,但是数据类型不一致会报错。
返回数据类型为void就不用写 return 返回值类型
:
返回数据类型(int)和返回的数据类型(String)不一致会报错:
(2).类
类是一个模板,它描述一类对象的行为和状态,在Java中Object类是所有类的基类(父类)。
(3).对象
对象是类的一个实例(对象不是找个女朋友),有状态和行为。例如,一条狗是一个对象,它的状态有:颜色、名字、品种;行为有:摇尾巴、叫、吃等。
下图中男孩(boy)、女孩(girl)为类(class),而具体的每个人为该类的对象(object):
关于类(模板的基本语法)
对于学生我们可以看成是一个类Student,对于学生这个类(模板),我们可以通过属性和方法进行简单的描述,属性描述学生的信息,这个学生叫什么名字,年龄,家庭地址,班级号等,这个类(模板)的属性越多,对应的对象(模板的实例)的信息就越详细,方法描述学生的行为,比如,吃饭,睡觉,喝水,跑步,对应的类的方法越多,相应的对象的就越生动形象。
类的语法结构如下:
public class 类名{
//属性
//方法
}
实例如下:
public class Student {
//属性
public String name;
public int age;
public int classNum;
public String address;
//方法(行为)
public void eat() {
System.out.println("吃饭");
}
public void stuInfo() {
System.out.println(toString());
}
public String toString() {
return "Student [name=" + name + ", age=" + age + ", classNum="
+ classNum + ", address=" + address + "]";
}
}
关于什么是匿名类
当我们要去使用某个抽象类/接口的时候我需要做的就是写一个子类来继承该抽象类/实现该接口,然后通过在子类中重写对应的方法,在外部通过实例化该子类,再调用相应的方法,从而实现了对该抽象类/接口的使用,匿名内部类就是不用去写这个抽象类/接口的子类,直接创建该子类,并重写相应的方法,从而实现了对该抽象类/接口的使用。
语法结构如下:
new 父类名或者接口名(){
//相应的方法重写
}
对象如何创建实例:
- 对象创建:
类名 对象名=new 类名();
//无参构造创建对象 - 对象的创建:
类名 对象名=new 类名(参数,参赛...);
//有参构造创建对象(类名(参数,参赛…)对应类的构造方法) - 方法调用:
对象.方法名()
- 对象属性的访问/赋值:
对象.属性名;
/对象.属性名=属性值;
(这种方式需要注意属性的访问权限,后面会用get,set进行访问和设置)
public class Test {
public static void main(String[] args) {
Student admin=new Student();
admin.name="张三";
admin.age=10;
admin.classNum=2;
admin.address="奈何桥";
//学生信息
admin.stuInfo();
//执行eat()方法
admin.eat();
}
}
运行效果:
关于对象的比较(==和equals()):
对于==
- 对于基本数据类型,
“==”
就只看两个数据的值是否相等
。比如两个int 型的变量,就只看两个变量的值是否相等。 - 对于引用数据类型,
“==”
就只看两个数据的堆内存地址
。比如两个new的User对象,new一次就新分配一个内存空间,因此两个new的User对象堆内存地址值就是不一样的。
对于equals()(从equals源码可知,equals()本质还是在做“==”
)
代码如下:
int a=10,b=10;
System.out.println(a==b);//true
System.out.println(Integer.valueOf(a).equals(b));//true
执行效果:
但是equals()可以进行重写:
执行结果:
从上面我们可以看到System.out.println(aString2== bString2);
的输出结果为false,而System.out.println(aString== bString);
输出的结果为true,因为String aString="测试";
这种方式其实就是在常量池中对这个值分配一个地址给它,当String bString="测试"
的时候,常量池中已经有了这个"测试"值,就把对应的地址分配给它,所以这样aString
和bString
的地址其实是一样的,所以返回true,但当创建对象的时候就不是这样了,这种情况是直接在堆内存中开辟了一块新的空间去储存,但是前面说的equals()
本质就是==
,为什么还是返回false这是因为String这个类重写了Object父类的equals()方法。
关于重写和重载:
重写:
重新写一个将原来方法进行覆盖了,调用的时候调用自己重写的方法重载:
多写一个或多个方法名相同的方法但是形参一定不同(形参的数据类型,形参的长度),返回数据类型可以不同,这样就可以同一个方法有多种加载方式。
//重写Object下的toString()
public String toString() {
return "Student [name=" + name + ", age=" + age + ", classNum="
+ classNum + ", address=" + address + "]";
}
//【1】重载toString()方法
public String toString(int a) {
return "Student [name=" + name + ", age=" + age + ", classNum="
+ classNum + ", address=" + address + "]";
}
//【3】形参的长度不同
public String toString(int a,int b) {
return "Student [name=" + name + ", age=" + age + ", classNum="
+ classNum + ", address=" + address + "]";
}
//【4】形参的数据类型不同
public String toString(String a) {
return "Student [name=" + name + ", age=" + age + ", classNum="
+ classNum + ", address=" + address + "]";
}
//【5】返回数据可以不同
public void toString(String a,int c) {
System.out.println( "Student [name=" + name + ", age=" + age + ", classNum="
+ classNum + ", address=" + address + "]");
}
(4)三大特性(封装,继承,多态)
封装:
封装(Encapsulation)是面向对象方法的重要原则,就是把对象的属性和操作(或服务)结合为一个独立的整体,并尽可能隐藏对象的内部实现细节。
- 属性私有化,公有访问,通过get(),set()接口进行访问和初始化
- 有参构造方法初始化对象—>多个参数一起进行初始化(减少访问次数),可创建多个不同形参的构造方法提供多种初始化方案
封装的优点:
- 提高代码的安全性。
- 提高代码的复用性。
- “高内聚”:封装细节,便于修改内部代码,提高可维护性。
- “低耦合”:简化外部调用,便于调用者使用,便于扩展和协作
例子(🔺对上面的例子进行封装操作):
Student部分的代码如下:
public class Student {
//属性
private String name;
private int age;
private int classNum;
private String address;
public Student() {
// TODO Auto-generated constructor stub
}
public Student(String name, int age, int classNum, String address) {
this.name = name;
this.age = age;
this.classNum = classNum;
this.address = address;
}
//方法(行为)
public void eat() {
System.out.println("吃饭");
}
public void stuInfo() {
System.out.println(toString());
}
public String toString() {
return "Student [name=" + name + ", age=" + age + ", classNum="
+ classNum + ", address=" + address + "]";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if (age>0&&age<300) {
this.age = age;
}
this.age=0;
}
public int getClassNum() {
return classNum;
}
public void setClassNum(int classNum) {
this.classNum = classNum;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
Test部分代码如下:
public class Test {
public static void main(String[] args) {
//通过无参构造方法初始化对象
Student admin=new Student();
admin.setName("张三");
admin.setAge(10);
admin.setClassNum(2);
admin.setAddress("奈何桥");
//通过有参构造方法初始化对象
Student admin2=new Student("李四",12,2,"孟婆汤");
//学生信息
admin.stuInfo();
//执行eat()方法
admin.eat();
}
}
执行效果:
小白白必看:
关于如何自动生成属性的get()set()方法:
勾选要具体要生成属性的get()set()方法,选择OK即可生成
关于如何自动生成有参构造方法:
选择要生成的有参构造函数的参数:
点击ok自动生成,如下:
注意:
当一个类中有了有参构造函数之后,java机制将不再自动生成无参构造方法。
在属性对应的set方法中,可以通过相应的if语句来保证数据的合理性(
不推荐使用
继承:
继承(英语:inheritance)是面向对象软件技术中的一个概念。它使得复用以前的代码非常容易,能够大大缩短开发周期,降低开发费用–
(合理使用继承就能大大减少重复代码,提高代码复用性)
。
Java语言是非常典型的面向对象的语言,在Java语言中继承就是子类继承父类的属性和方法,使得子类对象(实例)具有父类的属性和方法,或子类从父类继承方法,使得子类具有父类相同的方法
。父类有时也叫基类、超类
;子类有时也被称为派生类
。
在没有学习继承之前,我们看见一个对象,就编写一个对象的模板,学生模板,老师模板,警察模板,其实我们可以发现上面这些模板其实有很多相同的属性,方法,这样就让我们写了很多重复代码,为了解决这个问题推出了继承的概论,就上面问题,其实我们可以先写一个人类模板,然后其他模板继承人类模板,人类模板的属性方法就可以继承给它的子类,这样这些相同属性,公用的方法就无需编写。
例子动物的例子(如🔺上图所示):
创建Animal类
class Animal
{
public int id;
public String name;
public int age;
public int weight;
public Animal(int id, String name, int age, int weight) {
this.id = id;
this.name = name;
this.age = age;
this.weight = weight;
}
//这里省略get set方法
public void sayHello()
{
System.out.println("hello");
}
public void eat()
{
System.out.println("I'm eating");
}
public void sing()
{
System.out.println("sing");
}
}
子类继承父类的语法:
class 子类 extends 父类{//继承animal
}
注意: 在Java中,类的继承是单一继承,也就是说一个子类只能拥有一个父类,所以extends只能继承一个类。
而Dog,Cat,Chicken类可以这样设计:
class Dog extends Animal//继承animal
{
public Dog(int id, String name, int age, int weight) {
super(id, name, age, weight);//调用父类构造方法
}
}
class Cat extends Animal{
public Cat(int id, String name, int age, int weight) {
super(id, name, age, weight);//调用父类构造方法
}
}
class Chicken extends Animal{
public Chicken(int id, String name, int age, int weight) {
super(id, name, age, weight);//调用父类构造方法
}
//鸡下蛋
public void layEggs()
{
System.out.println("我是老母鸡下蛋啦,咯哒咯!咯哒咯!");
}
}
多态(同一个对象在不同的场景下有不同的形态):
一个类具有多个形态,根据对象的不同执行的动作也会不同(方法),比如父类Animal,有二个子类Dog,Cat,二个子类都重写了Animal的eat()方法,当父类指向Dog对象的时候
Animal animal= new Dog();
,执行的就是Dog的eat()【狗吃骨头】,当父类指向Cat对象的时候Animal animal= new Cat();
执行的就是Cat的eat()【猫吃鱼】。
条件:
- 在子父类中(继承关系),子类重写父类相关的方法
- 父类引用指向子类对象:
Animal animal= new Dog();
代码:
package com.dudu;
public class Animal {
private String name;
private int age;
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
public void eat(){
System.out.println("吃饭");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public static void main(String[] args) {
Animal animal = new Dog();
Animal animal2 = new Cat();
animal.eat();
animal2.eat();
}
}
class Dog extends Animal{
public Dog() {
super("小狗",3);
}
public Dog(String name, int age) {
super(name, age);
}
public void eat(){
System.out.println(super.getName()+"吃骨头");
}
}
class Cat extends Animal{
public Cat() {
super("小猫",4);
}
public Cat(String name, int age) {
super(name, age);
}
public void eat(){
System.out.println(super.getName()+"吃鱼");
}
}
运行效果:
该部分相关学习链接:
菜鸟教程多态性
关于抽象,接口
抽象:
在Java面向对象当中,所有的对象都是用类进行描绘的,但是并不是所有的类都是用来描绘对象的,如果一个类没有包含足够多的信息来描述一个具体的对象,这样的类就是抽象类。
抽象类的定义方式
abstract class 类名 {
}
- 抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象。
- 抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的。
- 抽象类中,可以有成员变量(全局变量)。
- 抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
- 抽象类的子类,必须重写抽象父类中所有的抽象方法,否则,编译报错。除非该子类也是抽象类。
抽象类与普通类的区别
- 抽象类使用abstract修饰;普通类没有abstract修饰
- 抽象类不能实例化;普通类可以实例化
- 抽象类可以包含抽象方法,也可以包含非抽象方法;普通类不能有抽象方法
关于抽象类是否可实例化问题
抽象方法的定义方式
抽象方法不能用private、final、static、native修饰
public abstract 返回值类型 方法名(参数);
抽象类的特征:
- 抽象类不能实例化对象,所以抽象类必须被继承才能使用,其他的功能和普通类相同。
- 一个类只能继承一个抽象类。
- 抽象类的修饰符不能是private。
- 抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
- 构造方法,类方法(静态方法,被static修饰的方法)不能声明为抽象方法。
- 抽象类的子类必须给出抽象类中的抽象方法的具体实现,除非该子类也是抽象类。
接口(对方法进行抽象):
在软件工程中,接口泛指供别人调用的方法。在Java中接口是一个抽象类型,比抽象类更加抽象,是抽象方法的集合。一个类通过继承接口的方式,从而继承接口的抽象方法。从定义上看,接口只是一个集合,并不是类。类描述了属性和方法,而
接口只包含方法(未实现的方法)和常量。
接口的语法:
public interface 接口名称 {
//声明常量
//抽象方法
}
接口的变量会被隐式的指定为public static final 变量(并且只能是public static final 变量,用private修饰会编译报错)
🔻(下面的错误是因为a是一个常量,常量必须进行初始化操作!)
接口的方法会隐式的指定为public abstract方法
如何使用接口:
语法:
【public】 class 要实现接口的类 implements 接口1,接口2{
//重新接口中的所有方法
}
- 注意:如果一个普通类要同时继承一个类和实现接口,应该先继承后实现(在Java中只能继承一个类,但是可以实现多个接口),否则就会语法报错。例如:public class Aextends B implementsC,D E…
- 实现接口必须重写接口里面的所有方法(接口里面的方法都是抽象方法)
🔺(抽象类和接口被继承/实现,必须重写抽象类/接口中的抽象方法!)
接口的注意事项:
-
接口中所有的方法不能有具体的实现
,也就是说,接口中的方法必须都是抽象方法。从这里可以隐约看出接口和抽象类的区别,接口是一种极度抽象的类型,它比抽象类更加“抽象”,并且一般情况下不在接口中定义变量。 - 在抽象类中,可以包含普通方法和抽象方法,但是
在接口中,所有的方法必须是抽象的
,不能有方法体
,比抽象类更加的抽象。接口规定一个类必须做什么而不规定他如何去做。
关于什么是方法体:
接口的特征:
- 接口中只定义抽象方法,这些方法默认都是public abstract的,在方法声明时可以省略这些修饰符。
- 在接口中定义实例变量,非抽象实例方法以及静态方法都是不允许的,接口中没有构造方法,也不能被实例化。
(接口中只有常量和抽象方法)
。 - 一个接口不能实现另一个接口,但是可以多继承其他接口。
- 接口必须通过类来实现它的抽象方法。
- 如果一个类不能实现完接口中的抽象方法,那么这个类我们应该设计为抽象类。
- 不允许创建接口的实例(接口不能被实例化),但是允许定义接口类型的引用变量引用实现该接口的类的实例(多态)
🔺(一个接口不能实现另一个接口,但是可以多继承其他接口)
🔺(不允许创建接口的实例(接口不能被实例化),但是允许定义接口类型的引用变量引用实现该接口的类的实例(多态))
//定义接口InterA
interface InterA
{
void fun();
}
//实现接口InterA的类B
class B implements InterA
{
public void fun()
{
System.out.println(“This is B”);
}
}
//实现接口InterA的类C
class C implements InterA
{
public void fun()
{
System.out.println(“This is C”);
}
}
class Test
{
public static void main(String[] args)
{
InterA a;
a= new B();
a.fun();
a = new C();
a.fun();
}
}
输出结果为:
This is B
This is C
🔺通过上面抽象类和接口的学习再看上面讲到的匿名对象应该就能够很清晰的理解了。
部分内容来源于
:知乎:Java抽象类和接口(详解)
(4) this关键字,super关键字,static关键字,final关键字
- this关键字指向
当前对象
- super关键字指向
父类对象
- static关键字指向
类
,可以将数据共享给对象使用,可以通过类名.静态方法/属性进行调用 - final关键字
表示不可修改
,修饰属性的时候为不可修改的属性就是常量
,当修饰类的时候,该类不可继承
,当修饰方法的时候该方法不可重写
。
子类对象继承父类的属性和方法:
如果父类有有参构造函数,可以通过下面的操作对对象进行初始化操作:
(5) 类的常用方法(String;StringBuffer,Calendar)
String类:
(1)字符串的创建
语法1: String =字符串的值; //存储在公共池中
语法2: String 变量名=new String();//存储在堆中
(2)字符串进行拼接:通过+
语法1: 字符串+字符串
语法2: 字符串.concat(字符串)
运行效果:
(3)字符串不可变
进行拼接操作的字符串会生成新的字符串不会更改以前的值,
字符串本质是一个char[]的常量
代码实例:
运行效果:
(4)字符串常用方法
StringBuffer和StringBuilder类:
当对字符串进行修改的时候,需要使用 StringBuffer 和 StringBuilder 类。和 String 类不同的是,StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象。
String、StringBuffer与StringBuilder之间区别
- String
String是一个字符数组 常量
- StringBuffer
StringBuffer保存的是一个字符数组 变量
StringBuffer扩容机制
StringBuffer线程安全
- StringBuilder
StringBuilder存放的内容为字符数组 变量
都是使用AbstractStringBuilder父类进行操作
线程不安全(速度更快,适合单线程操作)
创建StringBuffer(StringBuilder的创建和相应方法的使用和StringBuffer基本一致):StringBuffer 对象名= new StringBuffer ();
//无参构造
有参构造创建StringBuffer对象参照下图:🔻
注意一下:
StringBuilder stringBuilder=new StringBuilder(10);
表示创建一个容量为10(0~9)的StringBuilder对象(StringBuffer一样
)
StringBuffer的追加,插入,和删除:
- 当进行插入的时候,插入的对应序号如果有值,将会移到插入内容的末尾(下图insert(8,“Java”))
StringBuffer常用的方法:
Calendar类:菜鸟编程Java 日期时间
12.异常机制
你可以通过我的这篇博文了解这部分的知识:Java基础异常处理
13.集合(Set,List,Map)
数组存放数据时需要先设置存放数据的容量大小,并且在删除数据的时候效率较低,为了更好的对数据进行处理,根据数据结构的不同,线程的安全性等多方面考虑,Java集合Api提供了丰富的集合操作,让我们对数据进行更优的处理。
集合体系:
-
Java 集合框架主要包括两种类型的容器,一种是集合(Collection),存储一个元素集合,另一种是图(Map),存储键/值对映射。
Collection 接口又有 3 种子类型,List、Set 和 Queue,再下面是一些抽象类,最后是具体实现类,常用的有 ArrayList、LinkedList、HashSet、LinkedHashSet、HashMap、LinkedHashMap 等等。 - Java 集合框架提供了一套性能优良,使用方便的接口和类,java集合框架位于
java.util包
中, 所以当使用集合框架的时候需要进行导包。
Set和List的区别
- Set 接口实例存储的是无序的,不重复的数据。List 接口实例存储的是有序的,可以重复的元素。
- Set检索效率低下,删除和插入效率高,插入和删除不会引起元素位置改变 <实现类有HashSet,TreeSet>。
- List和数组类似,可以动态增长,根据实际存储的数据的长度自动增长List的长度。查找元素效率高,插入删除效率低,因为会引起其他元素位置改变<实现类有ArrayList,LinkedList,Vector> 。
List
List的主要实现:ArrayList, LinkedList, Vector。 ArrayList LinkedList Vector
ArrayList | LinkedList | Vector | |
底层实现 | 数组 | 双向链表 | 数组 |
同步性及效率 | 不同步,非线程安全,效率高,支持随机访问 | 不同步,非线程安全,效率高 | 同步,线程安全,效率低 |
特点 | 查询快,增删慢 | 查询慢,增删快 | 查询快,增删慢 |
默认容量 | 10 | / | 10 |
扩容机制 | int newCapacity = oldCapacity + (oldCapacity >> 1);//1.5 倍 | / | 2 倍 |
List常用常用方法:
实例练习:
public static void main(String[] args) {
//创建List对象
List<String> list=new ArrayList<String>();//多态
//集合初始化
for (int i = 0; i < 10; i++) {
list.add("数据:"+i);//数据0,数据1.....数据9
}
System.out.println("遍历集合:");
//遍历结合
for (String str : list) {
System.out.println(str);
}
System.out.println("获取序号为0的值:");
//获取集合指定位置的值
System.out.println(list.get(0));
System.out.println("获取集合末尾的值:");
System.out.println(list.get(list.size()-1));//list.size()获取集合的长度
System.out.println("序号为0的值被移除之后,序号0的值变为了:");
//删除序号为0的值
list.remove(0);
//获取序号0的值
System.out.println(list.get(0));
}
运行效果:
将上面的ArrayList换成LinkedList运行的结果一致:
- ArrayList和LinkedList操作方式一致(因为它们都是List接口下的实现类,常用的方法名都是一样的,但是具体的实现方法不一样,这就是多态的妙处)
- ArrayList和LinkedList虽然操作方式一致但是底层的数据结构是不一致的,一个是数组一个是链表(在使用的时候要根据二者的优缺点进行选择)
运行效果(结果是一致的):
Set
Set的主要实现类:HashSet, TreeSet。
HashSet、TreeSet、LinkedHashSet的区别:
HashSet | TreeSet | LinkedHashSet | |
底层实现 | HashMap | 红黑树 | LinkedHashMap |
重复性 | 不允许重复 | 不允许重复 | 不允许重复 |
有无序 | 无序 | 有序,支持两种排序方式,自然排序和定制排序,其中自然排序为默认的排序方式。 | |
时间复杂度 | add(),remove(),contains()方法的时间复杂度是O(1) | add(),remove(),contains()方法的时间复杂度是O(logn) | LinkedHashSet在迭代访问Set中的全部元素时,性能比HashSet好,但是插入时性能稍微逊色于HashSet,时间复杂度是 O(1)。 |
同步性 | 不同步,线程不安全 | 不同步,线程不安全 | 不同步,线程不安全 |
null值 | 允许null值 | 不支持null值,会抛出 java.lang.NullPointerException 异常。因为TreeSet应用 compareTo() 方法于各个元素来比较他们,当比较null值时会抛出 NullPointerException异常。 | 允许null值 |
比较 | equals() | compareTo() | equals() |
Set常用方法:
实例练习:
运行效果:
- 0并没有被加入进去,因为Set的数据不能重复
- Set集合存放数据的顺序是乱序
关于HashSet如何检测重复:
TreeSet:
实例练习:
public static void main(String[] args) {
//创建Set集合
TreeSet<String> set=new TreeSet();
set.add("数据:0");
set.add("数据:0");
set.add("数据:1");
//初始化数据
for (int i = 2; i < 10; i++) {
set.add("数据:"+i);
}
System.out.println("使用Iterator集合遍历:");
//通过Iterator遍历集合
Iterator<String> iterator=set.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
System.out.println("使用增强for遍历:");
for (String string : set) {
System.out.println(set);
}
//获取集合第一个元素的值
System.out.println("获取集合第一个元素的值:"+set.first());
//获取集合最后一个元素的值
System.out.println("获取集合最后一个元素的值"+set.last());
//删除集合第一个元素的值
System.out.println("删除集合第一个元素的值");
set.pollFirst();
//删除集合最后一个元素的值
System.out.println("删除集合最后一个元素的值");
set.pollLast();
//通过Iterator遍历集合
iterator=set.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
运行效果:
- 使用增强for运行遍历和iterator接口遍历完全不同,增强for只能获取集合中一组的值,不能获取单个值,并且遍历的次数为集合的长度
- set.first() //获取集合第一个元素的值
- set.last() //获取集合最后一个元素的值
- set.pollFirst() //删除集合第一个元素的值
- set.pollLast()删除集合最后一个元素的值
Map
Map 是一种把键对象和值对象映射的集合,它的每一个元素都包含一对键对象和值对象。 Map没有继承于Collection接口,从Map集合中检索元素时,只要给出键对象,就会返回对应的值对象。
Map 的常用实现类:HashMap、TreeMap、HashTable、LinkedHashMap、ConcurrentHashMap
HashMap、HashTable、TreeMap的区别:
HashMap | HashTable | TreeMap | |
底层实现 | 哈希表(数组+链表) | 哈希表(数组+链表) | 红黑树 |
同步性 | 线程不同步 | 同步 | 线程不同步 |
null值 | 允许 key 和 Vale 是 null,但是只允许一个 key 为 null,且这个元素存放在哈希表 0 角标位置 | 不允许key、value 是 null | value允许为null。当未实现 Comparator 接口时,key 不可以为null当实现 Comparator 接口时,若未对 null 情况进行判断,则可能抛 NullPointerException 异常。如果针对null情况实现了,可以存入,但是却不能正常使用get()访问,只能通过遍历去访问 |
hash | 使用hash(Object key)扰动函数对 key 的 hashCode 进行扰动后作为 hash 值 | 直接使用 key 的 hashCode() 返回值作为 hash 值 | |
容量 | 容量为 2^4 且容量一定是 2^n | 默认容量是11,不一定是 2^n | |
扩容 | 两倍,且哈希桶的下标使用 &运算代替了取模 | 2倍+1,取哈希桶下标是直接用模运算 |
Map常用方法:
实例练习:
运行效果:
- Map集合不能直接通过增强for获取,要通过对应的key获取对应的值,或者通过Entry的方式获取。
Java集合框架总结
14.IO流
你可以通过我的这篇博文了解这部分知识:JavaSE基础篇之-Java 流(Stream)、文件(File)和IO
15.多线程
你可以通过我的这篇博文了解这部分知识:Java基础多线程
🔻下面的内容适合快速入门:
线程的生命周期:
创建一个线程:
- 通过实现 Runnable 接口;
- 通过继承 Thread 类本身;
- 通过 Callable 和 Future 创建线程。【不写实例】
通过实现 Runnable 接口
public class ThreadTest implements Runnable{
private String name;
public ThreadTest(String name){
this.name=name;
}
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(this.name+"打印:"+i);
}
}
public static void main(String[] args) {
Thread thread1=new Thread(new ThreadTest("线程1"));
thread1.start();
Thread thread2=new Thread(new ThreadTest("线程2"));
thread2.start();
}
}
运行效果:
通过继承 Thread 类本身
运行效果:
创建线程的三种方式的对比
- 采用实现 Runnable、Callable 接口的方式创建多线程时,线程类只是实现了 Runnable 接口或 Callable接口,还可以继承其他类。
- 使用继承 Thread 类的方式创建多线程时,编写简单,如果需要访问当前线程,则无需使用 Thread.currentThread()方法,直接使用 this 即可获得当前线程。
从前面我们创建的线程可以看出运行并不是同步的:
synchronized的使用(同步互斥锁的使用):
运行效果:
16.网络编程(TCP【重点】;UDP)
什么是TCP
百度百科:
传输控制协议(TCP,Transmission Control Protocol)是一种
面向连接的、可靠的、基于字节流的传输层通信协议
,由IETF的RFC 793 [1] 定义。
TCP的三次握手保证了TCP的连接可靠性
什么是UDP
Internet 协议集支持一个
无连接的传输协议
,该协议称为用户数据包协议(UDP,User Datagram Protocol)。UDP 为应用程序提供了一种无需建立连接就可以发送封装的 IP 数据包的方法。RFC 768 [1] 描述了 UDP。
TCP编程实例:
JAVA 仿QQ聊天程序(附源码)Java TCP编程实例