1, 封装
2, 继承
3,多态
4, 抽象类
1, 接口的概念
2,接口的声明
3, 接口的实现
4,接口中成员变量的特点
5, 接口中成员方法的特点
6, 类和接口的多实现
7, 接口的继承
8,接口与类的区别:
9, 抽象类和接口的区别
10, 接口中的方法是否可以被重载和覆盖?
11, 接口的新特性——默认方法和静态方法(jdk8之后)
12, 标记接口
java是一个体系,包含:
java语言
运行在各种平台上的虚拟机
class字节码文件格式
java api(jdk中提供的api),类库
商业的或者三方优秀开源类库
文件扩展名是.java
使用Java编译器(javac.exe)编译源文件,得到字节码文件。
使用JavaSE平台中的java解释器(java.exe)来解释执行字节码文件。
使用Linux(windons)环境编译执行Java程序
JDK:它是Java开发运行环境,在程序员的电脑上当然要安装JDK;
JDK = JRE + 开发工具集(例如Javac编译工具等)
JRE:Java Runtime Environment它是Java运行环境,如果你不需要开发只需要运行Java程序,那么你可以安装JRE
JRE = JVM + JavaSE标准类库
JDK 包含 JRE 包含 JVM
跨平台特性
平台指的是操作系统 (Windows,Linux,Mac)。
Java程序可以在任意操作系统上运行,一次编写到处运行
实现跨平台需要依赖Java的虚拟机 JVM (Java Virtual Machine)
为什么可以跨平台?因为JAVA程序运行依赖虚拟机,而针对不同操作系统有不同版本的虚拟机
Java语言是跨平台的,Java虚拟机不是跨平台的
Java平台
Java SE (桌面程序)标准版
Java EE (Web程序)企业版
Java ME(移动设备)微型版 -- Android,IOS兴起后就很少使用了
源文件:编写Java源文件(我们也称之为源代码文件),它的扩展名为.java;
编译:然后通过编译器把源文件编译成字节码文件,字节码文件扩展名为.class;
为什么要编译?JAVA程序是由虚拟机运行的,我们所写的代码虚拟机不认识,我们要把自己写的代码翻译成虚拟机所认识的语言
运行:最后使用解释器来运行字节码文件。
定义:用来解释和说明程序的文字,注释是不会被执行的
分类:
单行注释: //注释内容
多行注释: /注释内容/
文档注释: /**注释内容*/
** 注意:
对于单行和多行注释,被注释的文字,不会被JVM解释执行
对于文档注释,可以被JDK提供的工具 javadoc 所解析,生成一套以网页文件形式体现的该程序的说明文档
单行注释可以嵌套使用,多行注释不能嵌套使用
定义:常量就是不变的数据量, 在程序执行的过程中其值不可以发生改变
整形常量默认是 int类型
在Java中,定义长整形数据如果值超过int取值范围后面要+L,否则是错误的
小数常量默认是 double类型
D后缀为double,F后缀为float
定义float类型的数据后面要 + f ,否则默认是double
变量是内存中装载数据的小盒子,你只能用它来存数据和取数据
变量名是标识符,这说明只要是合法的标识符都可以用来做变量名。
变量使用的注意事项
变量定义后可以不赋值,使用时再赋值。不赋值不能使用
变量使用时有作用域的限制。(局部变量和全局变量)
变量不可以重复定义
计算机存储单元
计算机中储存和运算的最小单位:一个字节,也就是1个字节(byte)
常用储存单位1B(字节) = 8bit(比特位)
1KB = 1024B
1MB = 1024KB
1GB = 1024MB
1TB = 1024GB
1PB = 1024TB
自动类型转换:表示范围小的数据类型转换成范围大的数据类型,这种方式称为自动类型转换
自动类型转换格式:范围大的数据类型 变量 = 范围小的数据类型值;
默认转换:byte、short、char —> int —> long —> float —> double
byte、short、char 相互之间不转换,他们参与运算首先转换为 int类型
强制类型转换:表示范围大的数据类型转换成范围小的数据类型,这种方式称为强制类型转换
强制类型转换格式:范围小的数据类型 变量 = (范围小的数据类型) 范围大的数据类型值;
+: 正号、加、连接字符串
++,--运算符后置时,先使用变量a原有值参与运算操作,运算操作完成后,变量a的值自增1或者自减1;
++,--运算符前置时,先将变量a的值自增1或者自减1,然后使用更新后的新值参与运算操作。
+= -= *= /= %=
+=, -=, *=, /=这样的赋值运算符包含了一个 强制转换 的操作,会将左右两边运算后的结果,强制类型转换后赋值给左边
** 注意:赋值运算符左边必须是变量
int n = 10;
byte by = 20;
by += n; // 运算完毕后,by的值为byte类型30, 相当于代码 by = (byte)(by + n);
== != < > <= >=
结果只能是true 和 false
字符间的比较,比较的是其 ASCII 值
浮点数 与 整数 比较,只要值相等,就返回 true
&与-----false&true-----False
|或-----false|true-----True
异或-----trueflase-----True
!非-----!true-----Flase
&&短路与-----false&&true-----False
||短路或-----false||true-----True
&& : 又叫短路运算符,A&&B,如果A为假,不会去计算B的值,直接得到结果为 false
& : A & B,即使A为假,也会计算B的值。
|| : 也是短路运算符,A || B,如果A为真,不会去计算B的值,直接得到结果为 true
| : A | B,即使A为真,也会计算 B 的值。
异或^ : 左右两边条件结果相同,结果就为false,左右两边条件结果不同,结果就为true;
语法:布尔表达式 ? 表达式1 : 表达式2
当布尔表达式的值为true,则返回表达式1的值,否则返回表达式2的值
运算符优先级
优先级 描述 运算符
1 括号 ()、[]
2 正负号 +、-
3 自增自减,非 ++、--、!
4 乘除,取余 、/、%
5 加减 +、-
6 移位运算 <<、>>、>>>
7 大小关系 >、>=、<、<=
8 相等关系 ==、!=
9 按位与 &
10 按位异或 ^
11 按位或 |
12 逻辑与 &&
13 逻辑或 ||
14 条件运算 ?:
15 赋值运算 =、+=、-=、=、/=、%=
16 位赋值运算 &=、|=、<<=、>>=、>>>=
位运算符
位运算符
位运算是直接对 二进制 进行运算
在位运算中,操作数必须是 整型
位异或运算符的特点:
一个数据对另一个数据位异或两次,该数本身不变。
任何数和自身异或,结果为0
任何数和0异或,结果为本身
<< 左移一位,相当于乘以2: 3 << 2 = 12 --> 322=12
右移一位,相当于除以2: 3 >> 1 = 1 --> 3/2=1
3 >>> 1 = 1 --> 3/2=1
同样的运算,位运算的效率高于算术运算
引用数据类型的变量定义及赋值格式:数据类型 变量名 = new 数据类型();
调用该类型实例的功能:变量名.方法名();
Scanner类:
导包:import java.util.Scanner;
创建对象实例:Scanner sc = new Scanner(System.in);
调用方法:
int i = sc.nextInt(); //用来接收控制台录入的数字
String s = sc.next(); //用来接收控制台录入的字符串
随机数类Random
方法简介
public int nextInt(int maxValue) //产生 [0,maxValue) 范围的随机整数,包含0,不包含maxValue;
public double nextDouble() //产生 [0,1) 范围的随机小数,包含0.0,不包含1.0。
Random使用方式:
import导包:import java.util.Random
创建实例格式:Random 变量名 = new Random();
赋值:a = 变量名.nextInt(maxValue);
for (j = 1; j <= i; j++) { // 内层循环
if (i > 4) { // 判断i的值是否大于4
break itcast; // 跳出外层循环
}
}
第二种定义格式
第二种方式和第一种类似,只是数组中每个元素的长度不确定,必须要new:arr[0] = new int[5];...
int[][] arr = new int[3][];
第三种定义格式
二维数组中定义了三个元素,这三个元素都是数组,分别为{1,2}、{3,4,5,6}、{7,8,9}
int[][] arr = {{1,2},{3,4,5,6},{7,8,9}};
二维数组内存
Arrays.toString(arr)
for(int n: arr)
System.out.println(n+", ");
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + ", ");
}
System.out.println(Arrays.asList(arr));
Arrays.asList(arr).stream().forEach(s -> System.out.println(s));//java8
//打印二维数组三种
for(int i=0;i<arr.length;i++) {
for(int j=0;j<arr[i].length;j++) {
System.out.print(arr[i][j]+" ");
}
System.out.println(); //换行
}
for(int[] a:arr) {
for(int b:a){
System.out.print(b+" ");
}
System.out.println();//换行
}
for(int i=0;i<arr.length;i++)
System.out.println(Arrays.toString(arr[i]));
//打印三维数组三种``
int [][][] arrays=new int[4][3][2];//在栈空间创建一个空间
for(int i=0;i<arrays.length;i++) {
for(int i1=0;i1<arrays[i].length;i1++) {
for(int i2=0;i2<arrays[i][i1].length;i2++) {
System.out.print(arrays[i][i1][i2]);
}
System.out.println();//二维换行
}
System.out.println();//三维换行
}
为什么要有方法:提高代码的复用性
什么是方法:完成特定功能的代码块
访问修饰符:方法允许被访问的权限范围。( public,protected,private,无修饰符 )
返回类型:void、任何数据类型
方法名:同变量名规则。小写;多个单词,第一个单词首字母小写,其余首字母大写 。myMethod
参数列表:可以省略。参数类型 + 参数名,...
可以使用 对象名 调用方法,静态方法是使用 类名 调用的
方法包括在类中,调用时先创建包含方法的类的对象,然后用对象再去调用方法。
创建对象:类名 对象名 = new 类名();
调用:对象名.方法名();
方法参数是 基本类型 ,传递的是值。(包含String类型),形式参数的改变对实际参数不影响
方法参数是 引用类型,传递的是内存地址值。(String类型除外),形式参数的改变对实际参数有影响
方法的定义只能放在类里面,不能嵌套定义。故而不能在主方法内直接定义方法
方法返回值是void,方法中可以省略return
方法一般在主方法的前面定义
调用方法的时候,返回值是void, 不能写在输出语句中
方法的重载:在同一个类中,方法名相同,参数列表不同。与返回值类型无关。
参数列表不同:
参数个数不同
参数类型不同
参数的顺序不同(算重载,但是在开发中不用),注意:必须是不同数据类型。相同类型不存在顺序不同
方法重载注意事项
重载和参数变量名无关
重载和返回值类型无关如void method(int x)与int method(int y)不是方法重载,不能同时存在
重载和修饰符无关
重载看 方法名 和 参数列表
例:
public void sum(int... n){}
参数列表中如果有两个以上的参数,可变参数一定在最后
可以将数组传递给可变参数列表
数组作为参数时,不能将多个值传递给数组的
一个方法的形式参数列表,只能有一个可变参数列表
方法名相同,一个参数是可变参数,一个参数是一维数组,这两个方法不是重载,因为 一个可变参数等价于相应类型的一维数组
就可以对可变参数列表,进行相应的数组操作,比如求长度
可变参数列表所在的方法,是最后被执行访问的
方法重载的时候,既可以定义有可变参数的同名方法,也可以定义有确定参数个数的方法,jvm调用方法时,会优先调用 有确定参数个数 的方法
隐藏类的实现细节,实现了信息的隐藏及安全性,方便修改和实现
提高了程序的模块化,提高系统独立性和软件的可重用性,且易于维护
具体实现是编写该类的人控制的,让使用者只能通过事先定制好的 方法 来访问数据,实现者可以方便地加入控制逻辑,限制对属性的不合理操作
封装的实现
public class Cat {
//成员属性:
//修改属性可见性---private 限定只能在当前类内访问,只能修饰成员变量
private String name;
public Cat() {
}
//创建get/set方法
//在get/set方法当中添加属性的限定
public void setName(String name) { //set方法一般没有返回值
this.name = name;
}
public String getName() {
return "我是一只名叫"+this.name+"的猫咪";
}
public int getMonth() {
return month;
}
public void setMonth(int month) { //对年龄进行限定
if(month<=0)
System.out.println("输入信息错误,宠物猫的年龄必须大于0");
else
this.month = month;
}
}
一种类于类之间的关系,使用已存在的类作为基础建立新类。
使用已存在的类的定义作为基础创建新类
新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但 不能选择性地继承父类,必须继承父类 所有开放的特征
使用 extends 关键字实现
不能为了继承某个功能而随意进行继承操作,必须要符合 “A is a B” 关系
子类会自动拥有父类所有 非 private 修饰的属性和方法
通过 子类对象 既可以调用自身的 非 private 修饰的成员,也可以调用父类的 非 private 修饰的成员
父类 不可以 访问子类 特有成员,即使是共有的
继承的出现提高了代码的复用性,提高软件开发效率。
继承的出现让类与类之间产生了关系,提供了多态的前提
在Java中,类只支持单继承,不允许多继承,一个类只能有一个直接父类
多个类可以继承一个父类:
class B extends A{}
class C extends A{} // 类B和类C都可以继承类A
在Java中,多层继承 是可以的,即一个类的父类可以再去继承另外的父类
class A{}
class B extends A{} // 类B继承类A,类B是类A的子类
class C extends B{} // 类C继承类B,类C是类B的子类,同时也是类A的子类
在Java中,子类和父类是一种相对概念,一个类是某个类父类的同时,也可以是另一个类的子类
子类的对象调用成员变量的时候,子类自己有,使用子类,子类自己没有则调用父类
子父类中出现了 同名 的成员变量时,在子类中需要访问父类中非私有成员变量时,需要使用 super 关键字
子类的对象调用方法的时候,子类自己有,使用子类,子类自己没有调用的父类
子类中出现与父类一模一样的方法时,会出现覆盖操作,也称为 override 重写、复写或者覆盖
父类中有:静态代码块,构造代码块,无参构造方法,静态属性。
子类中有:静态属性,静态代码块,构造代码块,无参构造方法
父类的引用变量指向子类对象:父类类型 变量名 = new 子类类型();
普通类多态定义的格式:父类 变量名 = new 子类();
抽象类多态定义格式:抽象类 变量名 = new 抽象类子类();
接口多态定义的格式:接口 变量名 = new 接口实现类();
同一个父类的方法会被不同的子类重写。在调用方法时,调用的为各个 子类覆盖后的方法
Person p1 = new Student();
Person p2 = new Teacher();
p1.work(); //p1会调用Student类中重写的work方法
p2.work(); //p2会调用Teacher类中重写的work方法
当变量名指向不同的子类对象时,由于每个子类覆盖父类方法的内容不同,所以会调用不同的方法。
成员变量
编译看左边(引用变量的声明类型),运行看左边(实际访问到的成员变量的值,也是由引用变量的声明类型来决定)
方法
编译看左边(引用变量的声明类型),运行看右边(实际访问到的方法,是由引用变量所指向的对象的实际类型来决定)
因为对于方法重载而言,虽然多个方法的方法名相同,但是我们的编译器,可以根据方法调用代码推断出,所要调用的那个方法的方法签名,从而根据方法签名(jvm唯一的),确定要调用的方法
因为在编译器编译的时候,无法知道,具体调用的是哪个方法的代码,只有当 jvm 具体真正执行到调用代码的地方,jvm才能知道调用的究竟是哪个方法
实现运行时多态:继承、方法覆盖/重写(override)、父类引用指向子类对象
if( !a instanceof Dog){…}
在Java中,一个没有方法体的方法应该定义为抽象方法,而如果一个类中含有抽象方法,则该类必须定义为一个抽象类
抽象类通常作为一个 框架(虽然在父类抽象类中,有些行为并不能具体确定,但是从类设计的角度将,我们能确定该类存在这样的行为),把子类将实现的抽象方法组织起来,简化或限制子类的设计
public abstract 返回值类型 方法名(参数);
abstract class 类名 {}
抽象类和抽象方法都需要被 abstract 修饰。抽象方法一定要定义在抽象类中。
static、final、private 不能与 abstract 同时出现。
抽象方法 不能有 方法体
抽象类 不一定 有抽象方法,但是含有抽象方法的类 必须是 抽象类
构造方法,类方法(用 static 修饰的方法),不能声明为抽象方法。
抽象类本身不能实例化(但是多态机制可以用子类实例化),不可以直接创建对象
原因:调用抽象方法没有意义
只有覆盖了抽象类中 所有的 抽象方法后,其子类才可以创建对象。否则该子类还是一个抽象类。
抽象类只定义了类的部分行为(包含具体行为), 这些行为是 子类共有的,其它行为由子类实现的抽象方法提供
抽象类的 成员变量:既可以变量,又可以是常量
抽象类的 构造方法:用于父类数据的初始化
子类继承抽象类时,构造方法不会被覆盖。 而且,在实例化子类对象时首先调用的是抽象类中的构造方法再调用子类中的。
因此,在抽象类中可以使用构造方法封装,所继承子类公共的东西。
抽象类的 方法:可以是抽象的,也可以是非抽象的,
如果不想重写抽象类里面的抽象方法,则子类也必须是抽象类
如果不是抽象类,则必须实现抽象父类的所有抽象方法
抽象类一定是个父类?
严格来说是的,不是父类的话就没有意义了,抽象类的抽象方法必须由子类来实现 本身只起到定义的作用
抽象类中是否可以没有抽象方法?如果可以,那么,该类还定义成抽象类有意义吗?为什么?
可以没有抽象方法,有意义,是为了不让该类创建对象,方法可以直接让子类去使用
在实际开发中,有的时候,不希望使用者,直接实例化出一个类的对象,可以将这个类定义为 abstract
接口是功能的集合,同样可看做是一种特殊的数据类型,是比抽象类更为抽象的类。
接口只描述所应该具备的方法,并没有具体实现,具体的实现由接口的实现类(相当于接口的子类)来完成。这样将功能的定义与实现分离,优化了程序设计。
记住:一切事物均有功能,即一切事物均有接口
使用 interface 代替了原来的 class 其他步骤与定义类相同
public interface 接口名称 extends 其他的类/接口名 {
//声明变量
//抽象方法
}
接口是隐式抽象的,当声明一个接口的时候,不必使用abstract关键字。
当类实现接口的时候,类要实现接口中所有的方法。否则,类必须声明为抽象类。
类使用implements关键字实现接口.
一个类如果实现接口, 有两种操作方法:
实现类为非抽象类, 就需要重写接口中所有的抽象方法.
实现类为抽象类, 可以不重写接口中的抽象方法。
class 类 implements 接口1,接口2... {
//重写接口中方法
}
四、 接口中成员变量的特点
接口中无法定义普通的成员变量.
接口中定义的变量,必须有固定的修饰符修饰public static final ,所以接口中的变量也称之为常量,其值不能改变。
static 可以被类名、接口名直接调用
final 最终,固定住变量的值
public static final 在接口中可以省略不写,也可以选择性写,但是不代表没有.
接口中的成员变量必须显式初始化.
interface Demo { ///定义一个名称为Demo的接口。
public static final int NUM = 3;// NUM的值不能改变
}
接口中的成员都是 public 的,不能指定其它的访问控制修饰符
接口中成员方法定义的固定格式: public abstract 返回值类型 方法名字(参数列表)
子类必须覆盖接口中所有的抽象方法后,子类才可以实例化,否则子类是一个抽象类。
类和接口的多实现是接口最重要的体现:解决多继承的弊端。将多继承这种机制在 java 中通过多实现完成了。
多继承的弊端:
多继承时,当多个父类中有相同功能时,子类调用会产生不确定性。
其实核心原因就是在于多继承父类中功能有主体,而导致调用运行时,不确定运行哪个主体内容。
为什么多实现能解决? 因为接口中的功能都没有方法体,都是由子类/实现类重写来明确
一个接口能继承另一个接口,和类之间的继承方式比较相似。接口的继承使用extends关键字,子接口继承父接口的方法。
在Java中,类的多继承是不合法,但接口允许多继承。
接口不能用于实例化对象。
接口没有构造方法。
接口中所有的方法必须是抽象方法。
接口不能包含普通成员变量,除了 static 和 final 变量。
接口不是被类继承了,而是要被类实现。
接口支持多继承。
相同点
抽象类和接口都位于继承的顶端,用于被其他类实现或继承.
都不能直接实例化对象.
都包含抽象方法,其子类都必须覆盖这些抽象方法.
区别
抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。
抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final类型的。
接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。
一个类只能继承一个抽象类,而一个类却可以实现多个接口。
二者的选择:
优先选用接口,尽量少用抽象类;
需要定义子类的行为,又要为子类提供共性功能时才选用抽象类;
重载: 对于接口中的方法重载,重载条件和普通类没有任何区别,只是重载的方法没有方法体
覆盖: 接口中的方法也可以覆盖(Override),但没有实际的意义,因为接口中不提供方法的实现
默认方法
可以在不影响已有类的情况下,修改接口
可以有方法实现
父类中的默认方法可以被子接口继承
子接口可以覆盖父接口中的默认方法,甚至还可以把父接口中的默认方法覆盖为抽象方法
实现接口后,因为默认方法不是抽象方法,所以可以不重写,但是如果开发需要,也可以重写
默认方法使用default 关键字,只能通过接口实现类的对象来调用。
注意:默认方法的访问权限也是默认public
静态方法
可以有方法实现
可以直接通过接口名来访问
静态方法没有方法覆盖,因为静态方法没有运行时多态
interface Test{
//这个是默认方法
default String get(String aa){
System.out.println("我是jdk1.8默认实现方法...");
return "";
}
//这个是静态方法
static void staticmethod(){
System.out.println("我是静态方法");
}
}
注意事项
接口默认方法、静态方法可以有多个。
默认方法通过实例调用,静态方法通过接口名调用。
default 默认方法关键字只能用在接口中。
默认方法可以被继承,如果继承了多个接口,多个接口都定义了多个同样的默认方法,实现类需要重写默认方法不然会报错。
静态方法不能被继承及覆盖,所以只被具体所在的接口调用。
标记接口是没有任何方法和属性的接口.
它仅仅表明它的类属于一个特定的类型,供其他代码来测试允许做一些事情。
标记接口作用:简单形象的说就是给某个对象打个标(盖个戳),使对象拥有某个或某些特权。
例如:
java.awt.event 包中的 MouseListener 接口继承的 java.util.EventListener 接口定义如下:
package java.util;
public interface EventListener {
}
标记接口主要用于以下两种目的:
建立一个公共的父接口:
正如EventListener接口,这是由几十个其他接口扩展的Java API,你可以使用一个标记接口来建立一组接口的父接口。 例如:当一个接口继承了EventListener接口,Java虚拟机(JVM)就知道该接口将要被用于一个事件的代理方案。
向一个类添加数据类型:
这种情况是标记接口最初的目的,实现标记接口的类不需要定义任何接口方法(因为标记接口根本就没有方法),但是该类通过多态性变成一个接口类型。
原文:https://www.cnblogs.com/Xieyingpeng/p/14178265.html