Java基础
1、运算符
算术运算符
符号 | 作用 | 备注 |
---|---|---|
+ | 加 | |
- | 减 | |
* | 乘 | |
/ | 除 | 除数得到的是商 |
% | 取余 | 取余得到的是余数 |
1.1.1、八大基本类型与引用类型
八大基本类型 | |
---|---|
byte | 从上到下从小到大 |
char | |
short | |
int | |
Lang | |
float | |
double | |
Boolean |
算术表达式中包含多个基本数据类型的值的时候,整个算术表达式的类型会自动进行提升 提升规则如下:
- byte类型,short类型和char类型进行计算时将被提升到int类型
- 整个表达式的类型自动提升到表达式中最高等级操作数同样等级
等级顺序:byte,short,char->int->long->float->double
- 高转低需要强制转换,低转高自动转换
1.1.2、”+” 操作
- 当 “ + “ 操作中出现字符串时,这个” + “是字符串连接符,而不是运算符
- System.out.println(“ itheima “ + 666 ); //结果是 itheima666
- 在 “ + “操作中,如果出现字符串,就是连接运算符,否则就是算术运算符。当连续进行” + “操作时,从左到右逐个执行。
- System.out.println(1 + 99 + “年黑马” ); //结果是100年黑马
1.1.3、自增 与 自减 ++ —
符号 | 作用 | 说明 |
---|---|---|
++ | 自增 | 变量的值加1 |
— | 自减 | 变量的值减1 |
- ++和—既可以放在变量的后边,也可以放在变量的前边
- 单独使用的时候,++和—无论是放在变量的前面还是后面结果是一样
- 参与操作的时候,如果放在变量的后边,先拿变量参与操作,后拿变量做++或者—。
- 参与操作的时候,放在变量的前边,先拿做++或—,后拿变量参与操作
- 最常见的用法:单独使用 | 符号 | 说明 | | —- | —- | | == | a==b,判断a和b的值是否相等,成立为true,不成立为false | | != | a!=b,判断a和b的值是否不相等,成立为true,不成立为false | | > | a>b,判断a是否大于b,成立为true,不成立为false | | >= | a>=b,判断a是否大于等于b,成立为true,不成立为false | | < | a<b,判断a是否小于b,成立为true,不成立为false | | <= | a<=b,判断a是否小于等于b,成立为true,不成立为false |
1.1.4、逻辑运算符
符号 | 作用 | 说明 |
---|---|---|
& | 逻辑与 | 有false全为false |
| | 逻辑或 | 有true就位true |
^ | 逻辑异或 | 相同为false不同为true |
! | 逻辑非 | 与结果相反 |
短路逻辑运算符
符号 | 说明 |
---|---|
&& | 左边为真,右边执行|左边为假,右边不执行 |
|| | 左边为假,右边执行|左边为真,右边不执行 |
//++运算符 当他们做完比较后还是要加上1
System.out.print(i++ > 100 & j++ >100);
1.1.5、三元运算符
- 格式:关系表达式?表达式1:表达式2
int a = 10;
int b = 20;
//a>b 如果a大赋值给max 如果b大赋值给max
int max = a > b ? a : b;
算法合集:
两只老虎
//判断两只老虎体重是否相同 Tiger1:180kg Tiger2:200kg
int tiger1 = 180;
int tiger2 = 200;
boolean j = tiger1==tiger2?true:false;
System.out.print(j);
三个和尚
//判断三个和尚那个身高最高
int height1 = 150;
int height2 = 210;
int height3 = 165;
//使用三元运算符 用临时数据来存前两个谁最高
int tempHeight = height1 > height2?height1:height2;
int maxHeight = tempHeight>height3?tempHeight:height3;
System.out.print(temp);
三个和尚升级版
//判断三个和尚那个身高最高
int height1;
int height2;
int height3;
//使用三元运算符 用临时数据来存前两个谁最高
Scanner sc = new Scanner(System.in);
height1 = sc.nextInt();
height2 = sc.nextInt();
height3 = sc.nextInt();
int tempHeight = height1 > height2?height1:height2;
int maxHeight = tempHeight>height3?tempHeight:height3;
System.out.print(temp);
奇偶数
Scanner sc = new Scanner(System.in);
int number = sc.nextInt();
if(number % 2 == 0){
System.out.print("输入的是偶数"+number);
}else{
System.out.print("输入的是奇数"+number);
}
考试奖励
int score = 80;
if(score >= 95 && score <= 100){
System.out.print("自行车");
}else if(score >=90 && score <=94) {
System.out.print("游乐场");
}else if(score >= 80 && score <= 89){
System.out.print("变形金刚");
}else{
System.out.print("挨打");
}
水仙花
需求:在控制台输出所有的”水仙花数”
什么是”水仙花数”:
水仙花是一个三位数
111 222 333 370 520 999
水仙花数的个位、十位、百位的数字立方和等于原数
123 13+23+3^3 1+8+27=36 不等于 123 不是水仙花数
371 33+73+1^3 27+343+1=371 等于 371 是水仙花数
分析:
- 三位数的个位数字如何求:
- 371 1就是原始数字对10进行取余运算的结果; 371 % 10 = 1;
- 三位数的百位数字如何求:
- 371 3就是原始数字除以100的结果(整除) 371 / 100 = 3
- 三位数的十位数字如何求:
- 371 371通过除以10,可以将7移动到个位上(整除) 371 / 10 = 37
- 37 通过对10进行取余运算可以得到最后一位的值7 37 % 10 = 7
- 思考:任意数字的指定位上的数值如何求
- 先使用整数操作将要求的数字移动到个位上,再使用取余操作取出最后一位的值
- 123456789 先整除10000得到12345,再对10取余得到5 ```java int a = 371;
- 先使用整数操作将要求的数字移动到个位上,再使用取余操作取出最后一位的值
int hundred = a / 100;//百位 int decade = a/10%10;//十位 int the_unit = a / 10;//个位
<a name="ee0cd968"></a>
### 珠穆朗玛峰
需求:世界最高山峰是珠穆朗玛峰(8844.43米=8844430毫米),假如我有一张足够大的纸,它的厚度是0.1毫米。
请问:我折叠多少次 答案27次
```java
double a = 0.1;
int zf = 8844430
while(true){
a *= 2;
if(a==8844430){
break;
}
}
逢七过
需求:1~100满足逢七过规则的数据都要跳过
思路:
- 数据在1~100之间,用for循环实现数据获取
- 根据规则,用if语句实现数据的判断:要么个位是7,要么十位是7,要么能够被7整除
x%10 == 7 x/10% 10== 7 x%7 == 0for(int i=0;i<=100;i++){
if( i%10==7 || i/10%10==7 || x%7==0){
sout(i)
}
}
不死神兔
需求:有一对兔子,从出生后第三个月起每个月生一对兔子,小兔子长到第三个月后又生一对兔子假如兔都不死,问第二十个月兔子对数为多少?
他的规律是 :
1+1+2+3+5+8·······;
后面的一个数是前面两个数的合我们只需要把前面两个数相加
/**
使用数组来做:
我们可以定义一个数组
由于我们已经知道第一个月和第二个月的数量是1
*/
int[] arr = new int[20];
arr[0] = 1;
arr[1] = 1;
//推论:
/**
arr[2] = arr[0] + arr[1];
*/
for(int x=2;i<=arr.length;i++){
//这样就可以实现当x=2的时候 2 = 1 + 1;
//arr[x] = arr[x-2] + arr[x-1]
//当x=3时 3 = 1 + 2
//当x=4时 4 = 2 + 3
//当x=5时 5 =
arr[x] = arr[x-2] + arr[x-1];
}
✳百钱百鸡
需求:我国古代数学家张丘建在《算经》一书中提出数学问题:鸡翁—值钱五,鸡母—值钱三,鸡雏三值钱一。百钱买百鸡,问鸡翁、鸡母、鸡雏各几何?
思路:
- 第一层循环,用于表示鸡翁的范围,初始化表达式的变量定义为x=0,判断条件是x<=20
- 第二层循环,用于表示鸡母的范围,初始化表达式的变量定义为y=0,判断条件是y<=30
- 这个时候,用于表示鸡雏的变量z=100-x-y
- 判断表达式z%3 == 0 和表达式5_x+3_y+z/3 =100是否同时成立如果成立,输出对应的x,y,z的值,就是对应的鸡翁,鸡母,鸡雏的值
//第一层循环,用于表示鸡翁的范围,初始化表达式的变量定义为x=0,判断条件是x<=20
for(int x=0;x<=20;x++){
//第二层循环,用于表示鸡母的范围,初始化表达式的变量定义y=0,判断条件是y<33
for(int y=0;y<=33;y++){
//定义一个表示鸡雏的变量
int z = 100 - x -y;
if(z%3==0&&5*x+3*y+z/3==100){
sout(x+"x"+y+"y"+z+"z");
}
}
}
冒泡算法
将数组进行排序
int[] arr = {25,36,12,85,15};
//让数组从小到大排序
//从0索引比大小到最后-1的索引
for(int i=0;i<length-1;i++){
for(int j=i;j<length;j++){
if(arr[x]>arr[i]){
int temp = arr[i];
arr[i] = arr[x];
arr[x] = arr[i];
}
}
}
2、数据输入Scanner
//三步曲
//1.导入包
import java.uitl.Scanner;
class ScannerTest{
public static void main(Stirng[] args){
//2.创建对象
Scanner sc = new Scanner(System.in);
//接收数据
int x = sc.nextInt();
}
}
//创建对象
Scanner sc = new Scanner(System.in);
//接收的数据是一个字符串数据
sc.nextLine();
//输出结果
3、循环语句
for语句格式:
for(初始化语句;条件判断语句;条件控制语句){
//循环体语句;
}
- 执行流程:
- 执行初始化语句
- 执行条件判断语句,看其结果是true还是false
- 如果是false,循环结束
- 如果是true,继续执行
- 执行循环体语句
- 执行条件控制语句
- 回到2继续
While语句格式:
while(判断语句块){ //判断语句块是true进入循环体 false不进入
//循环体语句
}
执行流程:
- 执行条件判断语句,看其结果是true还是false
- 如果是false,循环结束
- 如果是true,继续执行
- 执行循环体语句
- 执行条件控制语句
- 回到2继续
do····while语句:
do{
//循环体语句
}while(条件判断语句)
- 执行循环体语句
- 执行条件控制语句
- 执行条件判断语句,看其结果是true还是false
- 如果是false,循环结束
- 如果是true,继续执行
- 如果是true回到1继续执行
4、数组
int[] arr = new int[3];
sout(arr][3]);//这个会报错
数组常见报错:
- 数组的下标与长度不一样,数组长度为n时,下标就是n-1,如果要去获取n的下标就会报错。下标是从0开始的,长度是从1开始的。
- 报一个ArrayIndeOfBoundsExpectation 下标越界。
- 如果给数组赋值为null,然后去调用这个数组里面的元素就会爆空指针异常
- 会报错一个NullPointerException异常
数组排序:
int[] arr = {24,96,35,68};
//会自动排序 数字是从小到大 字母是从a到z
Arrays.sort(arr);
- 使用Arrays.sort();
5、构造方法
- 构造方法的创建
- 如果没有定义构造方法,系统将给出一个默认的无参构造方法
- 如果定义了构造方法,系统将不再提供默认的构造方法
- 构造方法的重载
- 如果自定义带参构造方法,还要使用无参构造方法,就必须再写一个无参构造方法
- 推荐的使用方式
- 无论是否使用,都手工书写无参数构造方法
6、String字符串
String类代表字符串,Java程序中的所有字符串文字(例如”abc“)都被实现为此类的实例
也就是说,Java程序中所有的双引号字符串,都是String类的对象。
6.1、字符串特点
- 字符串一旦创建不可变,它们的值在创建后不能被更改。
- 虽然String的值是不可变的,但是它们可以被共享。
- 字符串效果上相当于字符串组(char[]),但是底层原理是字节数组(byte[])。
6.2、String构造方法
方法名 | 说明 |
---|---|
public String() | 创建一个空白字符串对象,不含有任何内容 |
Public String(char[] chs) | 根据字符数组的内容,来创建字符串对象 |
public String(byte[] bys) | 根据字节数组的内容,来创建字符串对象 |
String s = “abc”; | 直接赋值的方式创建字符串对象,内容就是abc |
代码:创建方法 char 与 byte
String s1 = new String();
System.out.printIn();
//根据字符数组内容来创建字符串对象
char[] chs = {'a','b','c'};
String s2 = new String(chs);
//根据字节数组内容来创建字符串对象 字节会使用Unicode转换
byte[] byt = {97,98,99};
String s3 = new String(byt);//a b c
//直接赋值
String s4 = "abc";
6.3、String对象的特点
- 通过new创建字符串对象,每一次new都会申请一个内存空间,虽然内容相同,但是地址不同。
char[] chs ={'a','b}
String s1 = new String(chs);
String s2 = new String(chs);
上述代码:JVM会首先创建一个字符数组,然后每一个new的时候都会有一个新的地址,只不过是s1和s2参考的字符串内容是相同的
- 以 “ ” 方式给出的字符串,只要字符序列相同(顺序和大小写),无论在程序代码中出现几次,JVM都只会建立一个String对象,并在字符串池中维护
String s3 = "abc";
String s4 = "abc";
上述代码,针对第一行代码,JVM会建立一个String对象放在字符串常量池中。
第二行代码则让s4直接参考字符串常量池中的String对象,也就是说它们本质是同一个对象。
char[] chss ={'a','b','c'};
6.4、String字符串的比较
使用 == 做比较
- 基本类型:比较的是数据值是否相同
- 引用类型:比较的是地址值是否相同
字符串是对象,它比较内容是否相同,是通过一个方法来实现的,这个方法叫:equals()
- public boolean equals(Object anObject):将此字符串与指定对象进行比较,由于我们比较的是字符串对象,所以参数直接传递一个字符串
- 字符串遍历:
String line = sc.nextLine;
for(int i=0;i<line.length();i++){
line.charAt(i);
}
6.5、StringBuilder
如果对字符串进行拼接操作,每次拼接,都会构建一个新的String对象,即耗时,又浪费内存空间,而这种操作还不可避免。那么有没有一种比较好的方式可以解决这个问题呢?答案是肯定的,
String和StringBuilder
- String:内如不可变
- StringBuilder:内容是可变的
StringBuilder构造方法:
方法名 | 说明 |
---|---|
public StringBuilder() | 创建一个空白可变字符串对象,不含有任务内容 |
public StringBuilder(String str) | 根据字符串的内容,来创建可变字符串对象 |
StringBuilder链式编程
StringBuilder sb = new StringBuilder();
//由于sb.append返回的是一个对象就可以继续添加
sb.append().append().append().·········;
//返回相反的字符序列
sb.reverse();
StringBuilder 和 String 相互转换
- StringBuilder转换为String
- public String toString();通过toString()就可以实现把StringBuilder转换为String
//StringBuilder转换为String
StringBuilder sb = new StringBuilder();
sb.append("hello");
String s = sb.toString();
//String转换为StringBuilder
String s1 = "hello";
StringBuilder sb1 = new StringBuilder(s1);
- String 转换为StringBuilder
- public StringBuilder(String s):通过构造方法就可以实现把String转换为StringBuilder
7、集合
7.1、ArrayList
方法名 | 说明 |
---|---|
public ArrayList() | 创建一个空的集合对象 |
public boolean add(E e) | 将指定的元素追加到此集合的末尾 |
public void add(int index,E element) | 在此集合的指定位置插入指定的元素 |
ArrayList:
- 可调整大小的数组实现
- :是一种特殊的数据类型,泛型。
添加元素:
ArrayList<String> array = new ArrayList<Stirng>();
array.add("hhh");
array.add("ggg");
array.add("ddd");
//在指定的位置添加元素
array.add(1,"java");
- ※在指定位置添加元素如果那个位置有元素的话就会把那个元素往后挤一位后面所有的元素都会往后挪一位
- 添加元素可以在集合如何位置添加,但是集合只有10个元素下标为9,在11位置添加元素就会报错 集合越界。
ArrayList集合常用方法:
方法名 | 说明 |
---|---|
public Boolean remove(Object o) | 删除指定元素,返回删除是否成功 |
public E remover(int index) | 删除指定索引处的元素,返回被删除的元素 |
public E set(int index,E element) | 修改指定索引处的元素,返回被修改的元素 |
public E get(int index) | 返回指定索引处的元素 |
public int size() | 返回集合中元素的个数 |
ArrayList添加引用类型使用方法:
ArrayList array = new ArrayList<>();
Student st = new Student();
st.setName();
array.add(st);
array.get(0);//返回的是一个Student类型可以用Student接收
集合的通配符
ArrayList<?> array = new ArrayList();
7、泛型
泛型就是可以传任意参数
- 在类上面的泛型可以给一个T
- 在集合上面可以给一个指定的类型
下面代码显示类的泛型:
- T为泛型可以传入各种各样的数据类型包括其他类的引用
//泛型类
class Student<T>{
private T t;
public void setT(){
this.t = t;
}
public T getT(){
return t;
}
}
//测试类
//创建泛型实例然后往泛型方法中放入数据类型 我们测试引用数据类型和基本数据类型
class Test{
public static void main(String[] a){
//传入引用类型Teacher
Teacher t1 = new Teacher();
Student s1 = new Student();
Student s2 = new Student();
s1.setT(t1);//t1引用类型的地址就被放入其中
//然后用s1.getT()给t1中的a设置值
//设置值之前我们得把s1的泛型强制转换为String类型然后进行调用
((Teacher)s1.getT()).a=10;//这样t1的a就为10了
//下面我们来使用泛型对数组的玩法
int[] a ={12,32};
s1.setT(a);
//下面使用循环遍历出数组
for(int arr : (int[])s1.getT()){
System.out.print(arr)//输出结果是12,32
}
//使用集合来玩
ArrayList arr = new ArrayList();
arr.add("Hello");
arr.add("World");
arr.add("Java");
s1.setT(arr);
//使用遍历循环出来 如果集合没有给泛型指定泛型是什么类型for循环就的使用Object
//如果有指定泛型为String我们就这样写 String ar:(ArrayList<String>)s1.getT()
for(Object ar:(ArrayList)s1.getT()){
System.out.println(ar);// Hello World Java
}
//以上是引用类型的玩法基本类型的玩法更简单就不必演示了
//基本类型传什么进去输出什么出来不需要强制转换
s1.setT("1");
System.out.println(s1.getT());//输出1
}
}
class Teacher{
int a;
}
以上是玩法用法基本上用于同样的方法却要传不同的参数如下代码:
class Teacher{
//如果没有泛型的话 这样的重载方法很繁琐
public void show(String str){
System.out.print(str)
}
public void show(Integer i){
System.out.print(i)
}
public void show(Boolean b){
System.out.print(b)
}
//我们直接使用一个泛型方法
public void show(T t){
System.out.print(t)
}
}
测试类
class Test{
public static void main(String[] args){
Teacher t1 = new Teacher();
t1.show("");
}
}
8、继承的概述
继承的格式:
- 格式:public class 子类名 extends 父类名{}
- 范例:public class Zi extends Fu{}
- Fu:是父类,也被称为基类、超类
继承的好处和弊端:
继承好处:
- 提高了代码的复用性(多个类相同的成员可以放到同一个类中)
- 提高了代码的维护性(如果方法的代码需要修改,修改一处即可)
继承弊端
- 继承让类与类之间产生了关系、类的耦合性,当父类发生变化时子类实现也不得不跟着变化削弱了子类的独立性
继承方法重写注意事项:
- 私有方法不能被重写(父类私有成员子类不能被继承)但都可以调用
- 子类方法访问权限不能更低(public>默认>私有)
Java中继承的注意事项:
- Java中类只支持单继承,不支持多继承
- Java中支持多层继承
9、多态
多态中访问成员访问特点
- 成员变量:编译看左边,执行看左边
- 成员方法:编译看左边,执行看右边
10、基本类型包装类
考题:
class StringTest03{
public static void main(String[] args){
/*
一共有几个对象
s1 和 S2不是对象
一共三个对象:
方法区字符串常量池中有1个:"hello"字符串对象
堆内存当中有两个String对象
*/
String s1 = new String("hello");
String s2 = new String("hello");
}
}
Inner(内部类)
内部类的优点:对只在一处使用的类进行分组;提高封装性,增强代码可读性和可维护性。
内部类的分类:
成员内部使用:代码如下
class OuterClass{ //外部类定义
private int x = 100;
public class InnerClass{ //成员内部类定义
int y = 300;
public int calculate(){ //可以访问外层类的成员x
return x + y;
}
}
public void makeInner(){
InnerClass in = new InnerClass();//创建内部类对象
System.out.println(in.calulate);
}
}
注意事项:
成员内部类:
创建于生成的文件:
- 内部类编译后将会生成一个单独类文件,如上述代码编译后将生成两个文件:OuterClass.calss和OuterClass$InnerClass.class。
- 在成员内部类中可以定义自己的成员变量和方法也可以定义自己的构造方法,成员内部类可以定义类的修饰符
- 成员内部类并没有用static修饰且定义在外层类的类体中
- 成员内部类看成一个外层类的一个成员,所以可以访问外部内的所有成员,包括私有成员。而外部类不能不能直接访问内部类中的成员需要创建出对象的实例。
- 在外层类的中我们可以直接创建内部类实例,而在外层类的外面要想创建内部类的实例必须先创建一个外层类的对象,因为内部类对象对外层类对象有一个隐含的引用。
- 成员内部类中不能定义static变量和static方法
- 成员内部类也可以使用abstract和final修饰,其含义与其他类一样
- 成员内部类还可以使用private,public,protected或包可访问修饰符
下面代码展示内部类在外部类的外面创建实例:
class Test{
public static void main(String[] args){
//为了使用内部类首先我们得先创建出外部类
OuterClass outer = new OuterClass();
//使用外部类创建出内部类 代码如下
OuterClass.InnerClass inner = outer.new InnerClass();
System.out.println(in.calulate);
}
}
局部内部类:
定义在方法体或语句块定义类。**在方法体或语句块(包括方法、构造方法、局部方法、初始化块或静态初始化块)**内部定义的类称为**局部内部类**(local inner class)
局部内部类不能视作外部类的成员,只对局部块有效,同局部变量一样,`**在说明它的块之外完全不能访问和创建实例**`,因此也不能使用如何访问修饰符。但是照样可以调用外部类的所以成员。
- 局部内部类同方法局部变量一样,不能使用private,protected和public等访问修饰符,也不能使用static,但可以使用final或abstract修饰。
- 局部内部类可以访问外层类的成员,若要访问其所在方法的参数和就局部变量,这些参数和变量不能修改
- static 方法中定义局部内部类,可以访问外层类定义的static成员,不能访问外层的实例成员
- 在局部内部类中也可以定义自己的成员变量和方法也可以定义自己的构造方法
代码如下:
class Inner3{
private String x = "hello";
public void makeInner(int param){
class InnerClass{
final String y = "y";
public void seeOuter(){
System.out.println(y);
System.out.println(param);
test();
}
}
//局部内部类只能在创建他的这个方法体里面实例化对象
new InnerClass().seeOut();
}
public void test(){
System.out.print(x);
}
}
---------------------------------------------------------------
测试类如下:
class Test{
public static void main(String[] args) {
Inner3 i3 = new Inner3();
i3.makeInner(45);
}
}
输出结果是:
y
45
hello
局部内部类不能在局部块以外的位置实例化
匿名内部类:
定义类的最终目的:是创建一个类的实例,但如果某一个类的实例只需要使用一次,我们可以将类定义和实例一起创建出来 或者 说在定义类的时候就创建一个实例并调用其中的方法,以这种方式创建出来的类称为内部类。
声明和结构如下 TypeName为继承的类或者实现的接口匿名内部类必须从已有的类中继承后才能使用。:
new TypeNam(){
/*此处为内体*/
}
同样,也可以将构建的对象作为方法调用的参数
someMethod(new TypeName(){
/*此处为类体*/
});
匿名内部类没有名称所以没有构造方法,匿名内部类不能同时继承一个类和实现一个接口,要么继承一个类要么实现接口
接下来我们接收匿名类与匿名内部类:
class Cryptinym{
public void eat(){
System.out.println("吃东西");
}
}
测试类:
Class Test{
public static void main(String[] args){
//匿名类创建方式与调用方法
new Cryptinym().eat();
//匿名内部类创建方式和调用方式
new Cryptinym(){
//重写父类Cryptinym里面的eat方法
public void eat(){
System.out.println("匿名内部类调用吃饭")
}
}.eat();
}
}
匿名内部类本身就是继承类或实现接口所以它可以点出TypeName所有的成员。使用方法如下,说白了匿名类就是另一个类的子类
class Cryptinym{
int a ;
public void eat(){
}
//第三种匿名内部类在方法中创建
}
class Test{
public static void main(String[] args){
//第一种
//匿名内部类是继承的类所以说可以点出继承的这个类里面所有的成员
new Cryptinym(){
public void eat(){}
}.eat();
new Cryptinym(){
}.a=10;
//第二种匿名内部类创建方式
Cryptonym c = new Cryptonym() {
public void eat(){
System.out.println("1111");
}
};
c.eat();
}
}
匿名内部类可以是接口也可以是类
- 格式:
new 类名或者接口名(){
重写方法;
};
接口为什么可以被实例呢:本质是什么呢?
是一个继承了该类或者实现了该接口的子类匿名对象。
静态内部类:
静态内部类与成员内部类的行为完全不同,下面是它们的不同之处:
- 静态内部类中可以定义静态成员,而成员内部类不能
- 静态内部类只能访问外层的静态成员,成员内部类可以访问外层的实例成员和静态成员;
- 创建静态内部类的实例不需要先创建外层类的实例;相反,创建成员内部类实例,必须先创建一个外层类的实例
总结内部类:
class Outer{
class Inner{
}
}
class Test{
public static void main(String[] args){
/*成员内部类的创建
//创建一个内部类
//使用外部类创建出内部类
*/
Outer out = new Outer();
Outer.Inner in1 = out.new Inner();
//局部内部类创建方式 作用域只在当前这个方法中
class Inner{
public void test(){
System.out.print("你好");
}
}
Inner in = new Inner();
in.test();
//匿名内部类 new 那个类就是继承那个类
//匿名内部类继承了父类可以重写父类方法】
new Test(){
public void test(){
System.out.print("hello");
}
}.test();
}
}
接口:
如果一个方法的形参是接口名,他其实要得是该接口的实现对象
//使用匿名内部类不在使用创建一个类 直接调用j.jump然后创建了一个接口匿名实现对象传入修改了方法
j.jump(new Jump() {
@Override
public void jump() {
System.out.println("狗飞了");
}
});
//这个叫做创建了一个匿名的实现接口的类
new T(){
@Override
public void eat() {
System.out.println("你好");
}
}.eat();
枚举类型
在实际应用中,有些数据的取值被限定在几个确定的值之内。例如,一年有4个季度,一周有7天,一副牌有4种花色等。这种类型的数据,以前通过在类或接口中定义常量实现。
枚举类型是一种特殊的引用类型,它的申明和使用类和接口有类似的地方。它可以作为顶层的类型声明,也可以像内部类一样在其他类的内部声明,但不在方法中声明
- 枚举类的实例是常量,所以定义的时候我们使用大写
- 枚举类Enum类又是Object类的子类,同时实现了Comparable接口和Serializable接口,每个枚举类型都包含若干方法
常用方如下:
- public static E[] values():返回一个包含所有枚举常量的数组,这些枚举常量在数组是按照它们的顺序存储的。
- E valueOf(String name):传入一个字符串数据,查找常量中是否存在这个名字,如果找不到这个名字就会抛出IllegalArgumentException异常;
- public final int ordinal():返回枚举常量值的顺序值,该值是基于常量声明的顺序的,创建顺序第一个常量的顺序值是0,第二个是1,依次类推
- public fimal String name():返回枚举类的常量名字
- public String toString():返回枚举常量名
enum关键字是创建枚举类的:
enum poker{
A,K,Q,J
}
class EnumClass{
public static void main(String[] a){
//values使用方法创建一个枚举类的数组获取到
poker[] p = poker.values();
for(poker poker:p){
System.out.print(poker);
//按创建顺序输出 A K Q J
}
//valueOf使用方法
String A = "J";
System.out.print(poker.valueOf(A));//输出J
//常量是从创建0位置开始所以J是第三个 valueOf可以与ordinal()方法一起使用
System.out.print(poker.valueOf(A).ordinal);//输出3
//获取到A
System.out.print(poker.A);//输出A
}
}
可变参数
可变参数就是你可以任意的输入如何多个参数
//这个a是数组
public static int sum(int... a){
//当我们指知道可变参数是数组我们就来写一个加法不管外界传多少个值我们都可以把他们相加
int sum = 0;
for(int i:a){
sum+=i
}
return sum;
}
可变参数注意事项如果想和可变参数在声明一个形参就必须在可变参数前面声明
- 这里的变量其实是一个数组
- 如果一个方法有多个参数,包含可变参数,可变参数就必须放到最后
public static int sum(int... a,int b){}//这样写会发生
//下面代码进行更改
public static int sum(int b,int... a){}//这样写就不会报错
12、Collection集合进阶
集合:
collection单列接口
- list:可以重复 接口
- ArrayList 类 学习
- LinkedList 类 学习
- …….
- set:不可以重复
- HashSet 类
- TreeSet 类
- ….. 类
Map双列 接口
- HashMap 类 学习
- ….. 类
Collection集合
- 是单例集合的顶层接口,它表示一组对象,这些对象也称为Collcetion的元素
- JDK不是提供此接口的如何直接实现,它提供更具体的子接口(如Set和List)实现
- 创建collection
- 多态方式
- 具体的实现类ArrayList
Collection<String> c = new ArrayList<>();
c.add("hello");
c.add("world");
//清空所有元素
//c.clear();
c.isEmpty();//判断集合是否为空
System.out.println(c);
List集合:
ArrayList:
(有序的,有索引,可重复,底层是数据结构为数组)
底层数据结构是数组 增删慢,查询快
- 有序集合(也称为序列)用户可以精确控制列表中每个元素的插入位置。用户可以通过整数索引访问元素,并搜索列表中的元素
- 与Set集合不同,列表通常允许重复的元素
List集合特点:
- 有序:存储和取出的元素顺序一致
- 可重复:存储的元素可以重复
//list方法 与Arraylist使用方法一样
List<String> list = new ArrayList<>();
//添加 删除 修改
list.add("hhm");
list.add("zds");
list.add("wdy");
list.add("zds");
//它会删除第一个zds的位置
list.remove("zds");
//修改
list.set(2,"hhhh");
//可以获取
list.get();
//采用迭代器方式遍历
Iterator<String> it = list.iterator();
//获取是否有下一个元素
while (it.hasNext()){
//获取下一个元素
System.out.println(it.next());
}
ArrayList存储学生类使用compara方法 Collections.sort()
package com.collcetion.collections;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
public class CollectionStudent {
public static void main(String[] args) {
ArrayList<Student> arrayList = new ArrayList<>();
Student s1 = new Student("hhm",18);
Student s2 = new Student("zgr",18);
Student s3 = new Student("lqx",19);
Student s4 = new Student("ly",20);
arrayList.add(s1);
arrayList.add(s2);
arrayList.add(s3);
arrayList.add(s4);
Collections.sort(arrayList, new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
int num = s1.getAge() - s2.getAge();
int num2 = num==0?s1.getName().compareTo(s2.getName()):num;
return num2;
}
});
for(Student student:arrayList){
System.out.println(student.getName()+" "+ student.getAge());
}
}
}
LinkedList:
底层是链表 增删快,查询慢
方法名 | 说明 |
---|---|
public void addFirst(E e) | 在该列开头插入指定的元素 |
public void addLast(E e) | 将指定的元素追加到此列末尾 |
public E getFirst() | 返回此列表中第一个元素 |
public E getLast() | 返回此列表中最后一个元素 |
public E removerFirst() | 返回此列表中删除的第一个元素 |
public E removerLast() | 返回此列表中删除的最后一个元素 |
Set集合
Set集合特点 (无序的,不重复,没有索引,底层是哈希表)
- 对集合的迭代顺序不作任何保证,
- 不包含重复元素, 如果有重复元素就不会给放入集合里面去
- 没有索引的方法,所以不能使用普通for循环遍历
- Set是一个接口 底层数据结构是一个哈希表
Set集合练习
- 存储字符串遍历
package com.collcetion.list;
import com.arraylist.Student;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
/*
* Set是一个接口 它继承与Collection接口
*
* */
public class CollectionSet {
public static void main(String[] args) {
Set<String> set = new HashSet();
set.add("java");
//Set不能包含重复的元素 如果有重复元素就不会给放入集合里面去
set.add("hello");
set.add("hello");
//遍历
Iterator<String> iterator = set.iterator();
while (iterator.hasNext()) {
String next = iterator.next();
System.out.println(next);
}
}
}
HashSet
HashSet继承了Set接口Cloneable,Serializable
该类实现了HashSet接口,由哈希表支持 底层数据结构是哈希表
HashSet集合特点:
- 底层数据结构是哈希表
- 对集合的迭代顺序不作任何保证,也就是说不保证存储和取出的元素顺序一致
- 没有带索引的方法,所以不能使用普通的for只能使用增强for或者iterator迭代器
- 由于Set集合,所以是不包含重复元素的集合
HashSet集合练习:
public class HashSetDemo {
public static void main(String[] args) {
HashSet<String> set = new HashSet<>();
//添加元素
set.add("中");
set.add("国");
set.add("牛");
//Set不带索引所以用增强for或者迭代器
for (String s : set) {
System.out.println(s);
}
}
}
//Set是无序的
输出结果是:
牛
中
国
HashSet源码分析:为什么存两个相同字符串存不进去
首先通过add()把结点也就是元素把他传入进去后里面会有一个hash(key)这个方法会把这个字符串转换为一个hashCode值,
进行判断:根据对象的hash值计算对象的存储位置,如果该位置没有元素就直接存储元素,如果有元素就进行下面的判断:
- 1、存入的元素和以前的元素比较哈希值如果哈希值不同就继续向下执行,存入到集合里面。
- 2、如果哈希值相同,就会调用对象的.equals()方法进行比较
- 如果返回false,会继续向下执行,把元素添进去
- 如果返回true,说明元素相同不存储
Set存储学生类:
public class HashSetStudent {
public static void main(String[] args) {
HashSet<Student1> set = new HashSet();
Student1 s1 = new Student1("张国荣",18);
Student1 s2 = new Student1("hhm",20);
Student1 s3 = new Student1("zgr",21);
Student1 s4 = new Student1("张国荣",18);
set.add(s1);
set.add(s2);
set.add(s3);
set.add(s4);
for (Student1 s : set) {
System.out.println(s.getName()+"\t"+s.getAge());
//只有一个张国荣输出 说明student1类中的 hashCode 与 equals方被重写
}
}
public class Student1 {
private String name;
private Integer age;
public Student1(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
//这里重写了 hashcoed和equals的方法 所以构造方法传入名字和年龄一样的就不会被传入
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student1 student1 = (Student1) o;
if (name != null ? !name.equals(student1.name) : student1.name != null) return false;
return age != null ? age.equals(student1.age) : student1.age == null;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + (age != null ? age.hashCode() : 0);
return result;
}
}
LinkedHashSet
- 是由哈希表和链表实现的Set接口
- 不重复的是由哈希表来保证的
- 具有可预测的迭代次序:
- 意思是存储元素的顺序和取出元素的顺序是一致的这个是由链表来保证的
- 代码演示
class Test{
public static void main(String[] args){
LinkedHashSet<String> s = new LinkedHashSet<>();
s.add("hh");
s.add("jh");
s.add("hh");
for(Stirng s1:s){
System.out.print(s1)
//输出 hh jh
}
}
}
TreeSet
(元素有序(有条件),)
- 元素有序,这里的顺序不是指存储和取出的顺序,而是按照一定的规则进行排序,具体排序方式取决于构造方法
- TreeSet():根据其元素的自然排序进行排序
- TreeSet(Comparator comparator):根据指定的比较器进行排序
- 没有带索引的方法,所以不能使用普通for
- 因为是Set方法,所以不包含重复的元素
- 代码演示:
TreeSet的add()方法中含有一个compareTo(E e)这个方法每次添加一个数据都会进行比较排序
如果compareTo返回的是一个正数就会正序排序返回一个负数就会逆序排
如果要用学生年龄来排compareTo中有一个this方法使用上一个传进的age - 刚传入进来的age
this.age - age调用一次add就会调用一次compareTo
Map集合
- interface Map
K:键的类型;V:值的类型 - 将键映射到值的对象;不能包含重复的键;每个键最多可以映射一个值:就是一个键对应一个值
Map的实现类->HashMap
- 创建Map集合对象
- 多态方式
- 具体的实现类HashMap
- 当键最后一次出现的时候内容改变了,那个键指向的就是改变了的那个内容
方法:
方法 | 说明 |
---|---|
map.put(“01”,”中国人”); | 添加键与值 |
map.remove(“01”); | 删除键 就会删除值 |
map.clear(); | 移除所有的键值对元素 |
map.containsKey(“01”) | 判断是否包含指定的键 |
map.isEmpty() | 判断集合是否为空 |
map.size() | 集合的长度 |
HashMap遍历:
要么拿到key然后进行遍历
Set<String> s = map.keySet();
要么使用entrySet();
Set<Map.Entry<String, String>> entries = map.entrySet();
package com.collcetion.map;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/*
* 使用键去找值
*
* */
public class HashMapDemo1 {
public static void main(String[] args) {
Map<String,String> map = new HashMap<>();e
map.put("01","张学友");
//第一个张学友值就会被改变
map.put("01","中国人");
map.put("02","张国荣");
map.put("03","张国荣");
map.put("04","张国荣");
map.put("05","张家辉");
//我们获取到每一个键 然后用get方法获取到每一个键的值
Set<String> s = map.keySet();
for (String s1 : s) {
String value = map.get(s1);
System.out.println("值为:"+value+"\t 键为:"+s1);
}
//第二种遍历方式使用map.entry
//遍历的目的是为了拿到值
Set<Map.Entry<String, String>> entries = map.entrySet();
for (Map.Entry<String, String> entry : entries) {
String key = entry.getKey();
String value = entry.getValue();
System.out.println("键为:"+key +"\t"+ "值为:"+value);
}
}
}
学生类使用hashmap
package com.collcetion.map;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/*
*
* 保证创建出来的对象是唯一性
*
* */
public class HashMapStudent1 {
public static void main(String[] args) {
Student s1 = new Student("hhm",22);
Student s2 = new Student("zgr",22);
Student s3 = new Student("zxy",21);
Student s4 = new Student("hhm",22);
HashMap<Student,Integer> hs = new HashMap<>();
hs.put(s1,1);
hs.put(s2,2);
hs.put(s3,3);
hs.put(s4,4);
//获取键的集合
Set<Student> set = hs.keySet();
for (Student student : set) {
Integer i = hs.get(student);
System.out.println(" "+student.getName()+" " +student.getAge()+" "+i);
}
System.out.println("----------------");
}
}
集合嵌套
ArrayList嵌套HashMap
package com.collcetion.listmap;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;
/*
* 使用arraylist存储hashmap
*
* */
public class ArrayListHashMap {
public static void main(String[] args) {
ArrayList<HashMap<String, String>> list = new ArrayList<>();
HashMap<String, String> hs = new HashMap();
HashMap<String, String> hs1 = new HashMap();
HashMap<String, String> hs2 = new HashMap();
hs.put("何浩铭", "帅哥");
hs.put("张国0", "狗蛋");
hs1.put("ni", "帅哥");
hs1.put("张dsa", "狗蛋");
hs2.put("何dsf", "帅哥");
hs2.put("张sd1", "狗蛋");
list.add(hs);
list.add(hs1);
list.add(hs2);
//遍历arraylist集合,当你泛型存的是是什么的时候增强for循环就应该使用什么类型来接收,如果是学生类就得用学生类
//获取list里面的值 值是HashMap<String ,String>
for (HashMap<String ,String> hm:list){
//获取到HashMap这个值后我们就去取键
Set<String> strings = hm.keySet();
//取到键后遍历值 值是String类型
for (String k:strings){//这里这个k是键
//取值
String value = hm.get(k);//这里才是值
System.out.println(k+" "+value);
}
}
}
}
HashMap 嵌套 ArrayList
package com.collcetion.listmap;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;
/*
* 使用 hashmap 存储 arraylist
*
* */
public class HashMapArrayList {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
HashMap<String, ArrayList<String>> hs = new HashMap();
list.add("你好");
list.add("我好");
hs.put("001",list);
//遍历嵌套集合,先遍历键,把获得到的键输出Arraylist集合
Set<String> str = hs.keySet();
for (String s : str) {
ArrayList<String> arrayList = hs.get(s);
//获得到list集合了后遍历list
for (String arr:arrayList){
System.out.println(s+"键"+" "+arr+"值");
}
}
}
}
集合的数据结构:
常见的数据结构之栈:
- 数据进入栈模型的过程称为:压/进栈
- 数据离开栈模型的过程称为:弹/出栈
栈是一种数据先进后出的模型
常见的数据结构之队列:
- 数据从后端进入队列模型的过程称为:入队列
- 数据从前端离开队列模型的过程称为:出队列
队列是一种数据先进先出的模型
常见数据结构之数组
- 查询数据通过索引定位,查任意数据耗时相同,查询数据快 查询效率高
- 删除数据时,要将原始数据删除,同时后面每个数据前移,删除效率低
- 删除数据时如果有八个元素,当我们删除索引1位置元素后,后面所有的元素都得往前移一位
- 添加数据时,添加位置后的每一个数据后移,再添加元素,添加效率极低
- 添加数据时在索引的第二个位置添加后,后面的元素都得往后移一位
数组是一种查询快,增删慢的模型
常见的数据结构之链表
链表中元素称为:结点
链表是一种增删快的模型(对比数组)
链表查询数据是从头开始查询
结论:链表是一种增删快,查询慢的模型
增加结点的时候 是将某一个结点后面的地址改为增加结点的那个地址
删除也是
查询是从头开始
常见的数据结构之哈希表
哈希表
哈希值:是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值
Object类中有一个方法可以获取对象的哈希值
- public void hashCode():返回对象的哈希码值;
- Jdk8之前,底层采用数组+链表实现,可以说是一个元素为链表的数组
- Jdk8以后,在长度比较长的时候,底层实现了优化
- 初始化长度为16 0~15
- 数据是如何存储的:
- | 0 | 1 | 2 | 3 | ….. | 15 | | —- | —- | —- | —- | —- | —- |
- 例如”hello”这个字符串的哈希值是99162322
- 通过哈希值取余16得到的数就是他的位置 :99162322 % 16 = 2所以hello这个字符串在hashSet的第二个位置
- 接下来就是字符串”world”他的hash值是113318802%16=2也是2 所以在源码中就会进行比较哈希值是否相等 如果不相等就会直接放入2这个位置类似于下方的图片,小圆点代表链表,如相等就会继续进行比较上方的Set集合里面的源码。
- 接下来第三个元素”java”字符串也是余2所以他就会跟上面两个元素的哈希值作比较,如果都不相同就会放入
- 哈希值计算出位置 hashCoad() % 16 = ? =2就放入第二个位置
- 先比较哈希值,如果哈希值相同再去比较内容内容也相同就不会进行存储
- 哈希值不同直接存储
- 哈希值相同,内容不同进行存储
迭代器Iterator:
Collection<String> c = new ArrayList<Stirng>();
c.add("jaava");
//迭代器
Iterator<String> it = c.Iterator();
//Iterator.next()获取下一个元素 注意不能越界
it.next();
//hasNext方法是判断是否有元素
Iterator迭代器与for迭代器并发修改异常
for (int i = 0; i < list.size(); i++) {
String s = list.get(i);
if (s.equals("hhm")){
list.add("修改");
}
}
System.out.println(list);
//这个会报错 因为源码里面有一个实际修改次数与预期修改次数本来是相等的
//当你调用一次add()方法的时候实际修改次数就会增加 而预期次数还是没有增加 当调用next()
//next()方法会调用checkForComodification()这个方法里面就有一个判断就是 实际修改次数与预期修改次数 是否相等如果不相等就会报错 当调用了add实际修改次数就会+1,而使用get方法不会报错是因为get方法不会去调用checkForComodification()
while (iterator.hasNext()){
//第一次进入的时候这样实际==预期
//第二次调用的时候就报错了实际!=预期了 所以报错并发异常
String str = iterator.next();
if (str.equals("hhm")){
//当调用这个方法的时候实际就自增了1 预期还是原来的数
//所以回到上面
list.add("修改");
}
}
System.out.println(list);
获取列表迭代器
迭代器是不存在添加方法而,列表迭代器存在添加方法的
//获取列表迭代器
//不会出现并发异常是因为add在调用add方法的时候把实际修改次数赋值给预期修改次数所以不会出现并发异常
ListIterator<String> listIterator = list.listIterator();
while (listIterator.hasNext()){
//当第一次进入的时候实际 == 预期
//第二次进入的时候 listIterator中的add方法让预期 ==实际所以不会报错
String str = listIterator.next();
//如果循环遇到world就会进入
//
if(str.equals("world")){
//当调用add方法的时候 这里面有个定义是 预期 == 实际所以他们两个相等
listIterator.add("这里是迭代器的添加方法而不是list的");
}
}
System.out.println(list);
ListIterator源码:
三种遍历方式:
package com.collcetion.list;
import com.collcetion.Student;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/*
* 遍历学生类:
* 三种方式
* 1、普通for的遍历
* 2、迭代器Iterator
* 3、增强for遍历
* */
public class ListTraversal {
public static void main(String[] args) {
Student s1 = new Student("zs",18);
Student s2 = new Student("ls",18);
Student s3 = new Student("wmz",18);
List<Student> list = new ArrayList<>();
list.add(s1);
list.add(s2);
list.add(s3);
System.out.println("--------------");
//使用普通的for循环遍历
for (int i = 0; i < list.size(); i++) {
Student s = list.get(i);
System.out.println(s.getName()+" "+ s.getAge());
}
System.out.println("--------------");
//使用增强的for循环遍历
for (Student student : list) {
System.out.println(student.getName()+""+student.getAge());
}
System.out.println("--------------");
//使用Iterator循环遍历
Iterator<Student> iterator = list.iterator();
while (iterator.hasNext()) {
Student s = iterator.next();
System.out.println(s.getName()+""+s.getAge());
}
}
}
正则表达式(regular expression)
Stream流
- 获取Stream流
- 创建一条流水线,并把数据放到流水线上准备进行操作
- 中间方法
- 流水线上的操作。一次操作完毕后,还可以继续进行操作
- 终结方法
- 一个Stream流只能有一个终结方法,是流水线的最后一个操作
- 可以查看API文档关于流
N、继承、多态、抽象、封装
继承:
当子类继承了某个类之后,便可以使用父类中的成员变量,但是并不是完全继承父类的所有成员变量。具体的原则如下:
1)能够继承父类的public和protected成员变量;不能够继承父类的private成员变量;
2)对于父类的包访问权限成员变量,如果子类和父类在同一个包下,则子类能够继承;否则,子类不能够继承;
3)对于子类可以继承的父类成员变量,如果在子类中出现了同名称的成员变量,则会发生隐藏现象,即子类的成员变量会屏蔽掉父类的同名成员变量。如果要在子类中访问父类中同名成员变量,需要使用super关键字来进行引用。
继承super关键字:
如果父类使用了private修饰符我们就得使用super关键来给属性赋值
如果没有使用private来修饰我们用this来赋值
private修饰的 super关键字调用父类属性:
super.getWidth();
public class Rectangle {
double height;
private width;
public void setWidth(double width){
this.width = width;
}
public double getWidth(){
return this.width;
}
}
Cuboid继承与Rectangle
package com.oneself.Chapter_vii.W7_5;
public class Cuboid extends Rectangle{
double length;
public Cuboid(double length,double width,double height){
super(width);
this.length = length;
this.height = height;
}
//求长方体体积
public void volume(){
System.out.println(this.height);
System.out.println("长方形体积为:"+(this.height*this.length*super.getWidth()));
}
}
多态:
多态存在的三个必要条件
一、要有继承;
二、要有重写;
三、父类引用指向子类对象。
多态的好处:
1、可替换性(substitutability)。多态对已存在代码具有可替换性。例如,多态对圆Circle类工作,对其他任何圆形几何体,如圆环,也同样工作。
2、可扩充性(extensibility)。多态对代码具有可扩充性。增加新的子类不影响已存在类的多态性、继承性,以及其他特性的运行和操作。实际上新加子类更容易获得多态功能。例如,在实现了圆锥、半圆锥以及半球体的多态基础上,很容易增添球体类的多态性。
3、接口性(interface-ability)。多态是超类通过方法签名,向子类提供了一个共同接口,由子类来完善或者覆盖它而实现的。如图8.3 所示。图中超类Shape规定了两个实现多态的接口方法,computeArea()以及computeVolume()。子类,如Circle和Sphere为了实现多态,完善或者覆盖这两个接口方法。
4、灵活性(flexibility)。它在应用中体现了灵活多样的操作,提高了使用效率。
5、简化性(simplicity)。多态简化对应用软件的代码编写和修改过程,尤其在处理大量对象的运算和操作时,这个特点尤为突出和重要。
抽象:
抽象方法必须用abstract关键字进行修饰。如果一个类含有抽象方法,则称这个类为抽象类,抽象类必须在类前用abstract关键字修饰。因为抽象类中含有无具体实现的方法,所以不能用抽象类创建对象。
1)抽象方法必须为public或者protected(因为如果为private,则不能被子类继承,子类便无法实现该方法),缺省情况下默认为public。
2)抽象类不能用来创建对象;
3)如果一个类继承于一个抽象类,则子类必须实现父类的抽象方法。如果子类没有实现父类的抽象方法,则必须将子类也定义为为abstract类。
封装:
1、一个用户即使不看说明书,也可以猜到这个电筒的操作: 开关和充电。这个电筒用一个塑料壳将用户不需要接触的内部细节隐藏起来,只保留了两个接口,开关和电插头。使用这两个接口,用户足以使用该产品在设计中想要实现的功能。如果所有的细节都同时暴露给用户,那么用户会对产品感到不知所措 (比如下面不加壳的遥控器)。因此,封装提高了产品的易用性。
2、一个Java软件产品与一个日常产品相同。一个对象内部可以有许多成员(数据成员和方法)。有一些数据成员和方法只是内部使用。这时,我们会希望有一个给对象“加壳”的机制,从而封装对象。这样,用户可以比较容易学习和使用外部的接口,而不必接触内部成员。