标识符:
(1)类、接口、方法、变量等起名字时的字符序列。
(2)组成规则:大小写字符、数字字符、$和_
(3)不能以数字开头:int 1y=100;是不可行的。
(4)不可以的是Java关键字:int public=100;是不行的
Java严格区分大小写。
常见的命名规则:
驼峰法则:将所有字母都小写(包括缩写),然后将单词的第一个字母大写。每个单词的第一个字母都大写,来得到大驼峰式命名。除了第一个单词,每个单词的第一个字母都大写,来得到(小)驼峰式命名。
(1)见其名知其意;
(2)包的命名全部小写:h.l.studentdemo;
(3)类、接口的命名,每个单词的首字母全部大写:StudentDemo;
(4)方法、变量的命名,一个单词全部小写:student,多个单词组合小驼峰命名:getStudents;
(5)常量:一个单词全部大写:STUDENT,多个单词组合用_隔开:STUDENT_MAX_AGE。
注释:
(1)单行注释://
(2)多行注释:/*......*/
(3)文档注释:/**......*/
常量:
(1)定义:在程序执行过程中其值不可以发生改变的量
(2)分类:字面值常量和自定义常量
字面值常量:
A:字符串常量:"Is-Me-HL"
B:整数常量:所有的整数:3150 611 018
C:线束常量:所有的小数:12.0 3.14
D:字符常量:'a' 'A' '0'
E:布尔常量:true false
F:空常量:null
自定义常量:
(1)接口中定义:
public interface ConstantInterface {
String uesrname = "Is-Me-HL";
String illustration = "个人搭建于GitHub上的博客网址";
String website = "www.208j.club";
}
(2)枚举类中定义:
public enum ConstantEnum {
username("Is-Me-HL"), illustration("个人搭建于GitHub上的博客网址"), website("www.208j.club");
private String text;
private ConstantEnum () {
}
private ConstantEnum (String text) {
this.text = text;
}
public String getText() {
return text;
}
}
(3)直接使用final定义
public class Test {
private static final String username = "Is-Me-HL";
private static final String illustration = "个人搭建于GitHub上的博客网址";
private static final String website = "www.208j.club";
}
说明:所谓常量即只能初始化一次,不能重新赋值。实际上interface的属性默认的也是使用final和static,与第三种写法是一致的,同样枚举类型也是类似,用法上可以那么理解为static final,但枚举是枚举,类是类,枚举是值类型(被引用会创建副本,即便副本被修改了,原来的值也不会被修改),类是引用类型。
Java针对整数常量提供了4种表现形式:
二进制(0b)、八进制(0)、十进制(默认无)、十六进制(0x)
在计算机内部都是以二进制补码的形式来计算的,对于有符号数据的表示有三种:原码,反码,补码,那么举例如下:
用原码,反码,补码来分别表示+7和-7
首先写出7的二进制111,假设使用一个字节存储:8bit
原码:
+7:因为是正数最高位为0,即0000 0111 -7:因为是负数最高位为1,即1000 0111
反码:
+7:等于原码 -7:原码的符号位不变数值位取反 即 1111 1000
补码:
+7:等于原码 -7:反码的基础上加1 即1111 1001
数据类型:值类型(基本数据类型)和引用类型(复合数据类型)
值类型:整数类型(byte、short、int、long)、浮点类型(float、double)、布尔类型(boolean)、字符类型(char)。整型默认是int类型,浮点数默认是double类型。定义长整型long变量时,后缀要使用L或者l表示,定义单精度float类型变量时要记得加上F或f。
引用类型:除了值类型就是引用值类型了,类类型(class)、数组、接口类型(Interface)
关于数据的传递:
无论是基本数据类型的传递,或者是引用数据类型的传递,传过去的也都可以认为是一个副本,不同的是,基本数据类型传过去的是值,而引用类型传过去的是一个地址。无论在方法中对这两个副本进行何种操作修改,函数出来后,原来基本类型变量存放的还是之前在栈内存里开辟的某一块空间中里存放的的某一数值。同样引用类型变量在栈空间开辟的内存里面依旧是存放着开辟对内存时存放的地址值。但这时候两者不同的是,基本类型比如说int a=10;那么栈内存中就存放着10,在函数中一些列操作并不影响a所表示的10。但引用传进去的是地址,虽然出来后,引用变量依旧是同一个地址,但是谁能保证在函数中一些列的操作并不会影响你地址所指向的堆中某一内存内容的改变。那么函数出来后,同样的指向,内容就已经改变了。
就像你把自己家的钥匙拷贝了一份给了另一个人,这个人拿着钥匙在你家一顿瞎折腾,然后走了。等你拿着钥匙回到家以后,家里已经变成了被折腾过后样子。最后,总的一句话就是基本类型数据传递不会被改变样本,引用类型数据传递会改变样本,String类型虽是个类,但是做基本类型数据处理。
数据类型的运算:
+运算,做数据的加法。默认的转换是byte\short\char--int--long--float--double,byte,short,char互相之间不转换,他们参与运算首先转换为int类型。强制转换:目标类型变量名=(目标类型)(被转换的数据)
(1)byte b1=3,b2=4,b
byte b1=3,b2=4,b;
b=b1+b2;//错误
short b1=3,b2=4,b;
b=b1+b2;//错误
char b1=3,b2=4,b;
b=b1+b2;//错误
b1+b2两个变量相加先转化为int类型,然后再赋值给b,但是精度缺失,报错。
byte a;
a=3+4;//正确
a=3+400;//错误
常量相加,首先算出结果在看结果是不是在btye范围内,在的话就赋值给a,不在就报错。
(2)byte b=130;有没有问题?如果我想让她赋值正确,那么可以怎么做,结果是多少呢?
不正确,超出了范围127,那么byte的值会变成多少呢?130=10000010
byte不够130,所以转化为int----00000000 00000000 00000000 10000010
因为赋值给的是byte,所以计算机只看到补码10000010,
那么真正的值就是算出原码,就是我们在控制台下看到的:反码:10000001,原码11111110,即-126
(3)字符:'a'=97,'A'=65,'0'=48
"hello"+'a'+1-------helloa1
'a'+1+"hello"-------98hello
"5+5="+5+5--------5+5=55
5+5+"=5+5"--------10=5+5
(4)short s=1;s=s+1;//错误,这个式子会报精度缺失。
short s=1;s+=1;//正确,不是等价于s=s+1。而是等价于s=(s的类型)(s+1)。
逻辑运算符:
(1)&逻辑与:有false则false
(2)|逻辑或:有true则true
(3)^逻辑异或:相同的为false不同的为true
(4)!逻辑非:取反
注意:&和&&的区别?|和||的区别?
两者的最最终结果是一样的。但是&&有个短路效果,左边是false的,右边就不知行了
int a = 3, b = 4;
if ((a++ == 4) && (b++ == 4)) {
System.out.println(a + "\t" + b);
} else {
System.out.println(a + "\t" + b);//结果a=4,b=4
}
int a = 3, b = 4;
if ((a++ == 4) & (b++ == 4)) {
System.out.println(a + "\t" + b);
} else {
System.out.println(a + "\t" + b);//结果a=4,b=5
}
&& 短路的意思就是当左边的表达式足够判断真假时,右边就不执行,效率高。而&是把表达式全部执行了,再合并判断。同理||,短路或当左边判断为true,则右边就不会判断了,整个表达式直接为true
(5)逻辑异或:一个数对两个数据异或两次,这结果还是这个数。
小例题:实现两个数交换:int a=10,b=20;
方法一:使用中间变量。
int a = 10, b = 20;
int temp = a;
a = b;
b = temp;
System.out.println("a=" + a + "," + "b=" + b);
方法二:用异或实现。
int a = 10, b = 20;
a = a ^ b;
b = a ^ b;
a = a ^ b;
System.out.println("a=" + a + "," + "b=" + b);
方法三:变量相加的做法。
int a = 10, b = 20;
a = a + b;
b = a - b;
a = a - b;
System.out.println("a=" + a + "," + "b=" + b);
方法四:一句话实现交换。
int a = 10, b = 20;
b=(a+b)-(a=b);
System.out.println("a=" + a + "," + "b=" + b);
(6)<<:左移 左边最高位丢弃,右边补齐0;左移n位相当于乘x的n次方。
(7)>>:右移 最高位是0,右边补齐0,最高位为1,左边补齐1,相当于除以2的n次方。
(8)>>>:无符号右移 无论最高位是0还是1,左边补齐0。
用最有效的方式可以计算出2乘以8的结果?2*8 2<<相当于2*2^3
(9)三目运算符
比较两个数:a>b?a:b
比较三个数找最大值:int temp=(a>b)>a:b; int max=c>temp?c:temp;------写成一步:(a>b)?((a>c)?a:c):((b>c)?b:c)
注意:三木运算符能实现的,都可以使用if语句实现,反之不成立!当if语句控制的操作是一个输出语句的时候就不能,因为三元运算符操作完就应该有一个结果,而不是一个输出。
程序流程控制:(if...else/switch..case/while/do..while)
这一段中基本的语法就不一一写了,记录下自己相关需要注意的地方。
(1)switch:表达式的取值是有限定的,btye、short、int、JDK5以后可以是枚举,JDK7以后可以是String
switch语句的注意事项:
A:case后面只能是常量,不能是变量,多个case后面的值不能出现相同。
B:default可以省略吗?可以省略,但是不建议,因为它的作用是对不正确的情况给出提示。
C:break可以省略吗?可以省略,但是结果可能不是我们想要的,会出现一个现象:case穿透,所以不能轻易省略。
所谓case穿透:就是由于在某个case中没有break跳出语句,所以如果该case后还有case,就会出现case穿透,就会执行接下来的case,直到遇到其他case中的break语句,才结束。
(2)while循环和for循环的区别?使用区别:如果你想在循环结束后,继续使用控制条件的那个变量,用while循环,否则用for循环。因为变量及早的从内存中消失,可以提高内存的使用效率。如果是一个范围的,用for循环非常明确,如果是不明确做多少次,while循环较为合适。循环语句的区别:do....while循环至少执行一次循环体。而for、while循环必须先判断条件是否成立,然后决定是否执行循环体语句。
数组:
(1)数组的定义有两种方式int []a和int a[]:
第一种是:定义一个int类型的数组a变量。第二种是:定义一个int类型的a数组变量。第一种常用。
(2)数组的初始化有两种方式:
动态初始化:int[] arr=new int[4]。静态初始化:int[] arr=new int[]{1,2,3},[]中的值由系统自己确定。也可以简写成int[] arr={1,2,3}
(3)二维数组:是元素为一维数组的一个数组
格式1:数据类型[][]数组名=new 数据类型[m][n]:m:表示这是二维数组有多少个一维数组,n:表示每一个一维数组元素有多少个
注意:以下格式也可以表示已和二维数组
a:数据类型 数组名[][]=new 数据类型[m][n]; b:数据类型[] 数组名[]=new 数据类型[m][n];
int [][] arr=new int[3][2];
System.out.println(arr);
System.out.println(arr[0]);
System.out.println(arr[1]);
System.out.println(arr[2]);
System.out.println(arr[0][0]);
System.out.println(arr[0][1]);
[[I@3d4eac69
[I@42a57993
[I@75b84c92
[I@6bc7c054
0
0
int [][] arr=new int[3][];
System.out.println(arr);
System.out.println(arr[0]);
System.out.println(arr[1]);
System.out.println(arr[2]);
[[I@3d4eac69
null
null
null
这种写法是动态为以为数组开辟空间,该代码应为还没有为每一个一位数组分配在空间,所以arr[0\1\2]=null(关于值是null不是0,是因为arr[0\1\2]原本存储的是一维数组的地址,是个复合类型的数据,所以为null)。关于二维数组的使用,在初始化的时候,要记得二维数组中的元素一维数组的初始化,如果仅仅是int [][] a=int new[4][]的话,只是相当于向二维数组a中添加了四个一维数组的引用,且这些引用并没有指向对应的一维数组的,在你给该二维数组赋值的时候会报空指针异常错误。这个要注意。
小例子:
int[] arr = new int[2];
arr[0] = 100;
arr[1] = 120;
System.out.println(arr[0]);
System.out.println(arr[1]);
int[] arr1 = arr;
arr1[0] = 50;
arr1[1] = 2147483647;
System.out.println(arr[0]);
System.out.println(arr[1]);
100
120
50
2147483647
说明:创建一个变量会在栈内存中开辟一个空间,new一个对象会在堆上开辟空间。栈内存的两个变量指向同一个堆内存空间,无论是他们谁的操作,都针对同一个地方操作,也就是说其中一个对该指向的堆内存做了修改,另一个指向该内存的变量,变量值不变,但变量指向的堆内存中的内容同步改变。
java中参数传递的问题(小结):
基本类型:形式参数的改变对实际参数没有影响。引用类型:形式参数的改变直接影响实际参数。
面向对象知识点总结:
(1)Student p=new Student();
首先会去加载Student这个类,找到后,就把方法区里面的Student成员变量放入刚在堆中开辟的空间中,并且把成员方法的地址一块放入该空间中,成员方法依旧放在方法区。需要调用其某一个方法时,把方法调入到栈内存中,用完就消除。
(2)成员变量和局部变量的区别:
A:在类中的位置不同:成员变量:在类中方法外,局部变量,在方法定义中或者申明上
B:在内存中的位置不同:成员变量:在堆内存中,局部变量在栈内存中
C:生命周期不同:成员变量随着对象的创建而存在,随着对象的消失而消失,局部变量,随着方法的调用而存在,随着调 用的结束而结束。
D:初始化不同:成员变量:有默认初始化值,局部变量,没有默认初始化值,必须定义赋值然后才可以使用。
(3)匿名对象:(就是没有名字的对象)
new Student(),这个就是匿名对象,就是没有名字。Student s=new Student(),这个不是匿名对象。
匿名对象的应用场景:调用次数仅仅是一次的时候,其好处是匿名对象调用完毕就是垃圾,可以被垃圾回收器回收,占内存中不存在,匿名对象也可以作为参数传递。
(3)this:是当前类的对象引用,简单的理解他就代表当前类的一个对象。需要注意的是:谁调用这个方法,在该方法内部的this就代表谁。this的出现能够解决局部变量隐藏成员变量这一问题。比如一个类的含参构造函数常会这样写this.name=name;name表示的是局部变量,this.name指向的是这个要初始化的这个类中的成员变量。
(4)针对多个对象由共同的这样的成员变量值的时候,Java提供了关键字static
static的特点:(可修饰成员变量和成员方法)
A:随着类的加载而加载。B:优先于对象的存在。即在类实例化(也就是对象创建)之前就已经存在了。
C:被类的所有对象共享。D:可以通过类名.成员变量直接调用,当然也可以通过对象调用
静态修饰的内容一般称其为:与类相关的,类成员
注意事项:
A:在静态方法中是没有this关键字的(可以这么理解:静态的成员是随着类的加载而加载的,this是随着对象的创建而创建的,静态比对象先创建)
B:静态方法只能访问静态的成员变量和静态的成员方法。(同样也可以这么理解,静态随着类的加载而加载,如果非静态成员在静态方法中被访问了,那是找不到的呀)
C:当然此时可能会在想那非静态方法呢?当然是可以访问静态成员和非静态成员的啦(理解依旧可以同上理解)
(5)静态变量和成员变量的区别
A:所属不同,静态变量属于类,所以也成为类变量。成员变量属于对象,所以也成为实例变量(对象变量)
B:内存中的位置不同,静态变量存储于方法区的静态区,成员变量存储于堆内存。
C;内存中出现的时间不同,静态变量随着类的加载而加载,随着类的消失而消失。成员变量随着对象的创建而创建,随着对象的消失而消失。
D:调用的方式不同,讲台变量可以通过类名直接调用,也可以通过对象调用,成员变量只能通过对象名调用
(6)解析下面这句话
public static void main(String[] args){...}
public :公共的,访问权限最大,由于main方法是被JVM调用,所以要保证权限足够大
static:静态的,不需要创建对象,通过类名就可以,方便JVM调用
void:方法的返回值是给调用者使用的,而main方法的调用者是JVM,返回内容给JVM没有任何意义
main:是一个常见的方法入口,几乎所有的语言都是以他作为入口的
String[] args:这是一个字符串数组,早期为了接受键盘录入的数据,格式为jaba mainDemo hello world Is-Me-HL,这样的输入打印出来的就是hello、world、Is-Me-HL
(7)代码块:在Java中使用{}括起来的代码称为代码块
根据其位置和申明的不同,可以分为:局部代码块、构造代码块、静态代码块。
局部代码块:局部位置,用于限定变量的生命周期。
构造代码块:在类中的成员位置,用大括号括起来的方法,每次调用构造方法执行前,都会执行构造代码块:作用:可以把多个构造方法中的共同代码放到一起,对对象进行初始化。
静态代码块:在类中的成员位置,用{}括起来的代码,只不过他用static修饰了。作用:一般是对类进行初始化。
具体的写法例子(自行参考度娘)
例题:静态代码块、构造代码块和构造方法的执行顺序,不多说直接上代码,跑跑看。
class Son {
static {
System.out.println("Son 静态代码块");
}
{
System.out.println("Son 构造代码块");
}
public Son() {
System.out.println("Son 构造方法");
}
}
public class TestDemo {
public static void main(String[] args) {
Son s = new Son();
}
}
程序结果顺序是:Son 静态代码块、Son 构造代码块、Son 构造方法,显然先静态代码块-->构造代码块-->构造方法。需要注意的是:静态代码块就在类加载的时候执行一次、构造代码块:每次调用构造方法前都执行。
(8)继承:A:提高了代码的复用性。B:提高了代码的维护性。C:让类与类之间产生了关系,是多态的前提,但这也是继承的一个弊端,类的耦合性。
Java中继承的特点:A:Java只支持单继承,不支持多继承。B:Java支持多层继承(爸爸继承爷爷,儿子继承爸爸)。
Java中子类只能继承父类所有的非私有的成员(成员变量和方法),且不能继承父类的构造函数,但是可以通过super关键字去访问父类的构造方法。
(9)this和super的区别:this代表本类对象的引用。super代表父类空间存储的标识(可理解为父类引用,可以操作父类的成员)
A:调用成员变量:this.成员变量 调用本类的成员变量。super.成员变量 调用父类的成员变量
B:调用构造方法:this(...)调用本类的构造方法。super(...)调用父类的构造方法
C:调用成员方法:this.成员方法 调用本类的成员方法。super.成员方法 调用父类的成员方法
(10)继承中构造方法的关系
子类中所有的构造方法默认都会访问父类的空参数构造方法。为什么呢?因为子类会继承父类中的数据,可能还会使用父类的数据,所以在子类初始化之前,一定会完成父类数据的初始化。需要注意的是:子类每一个构造方法的第一个语句默认都是super(),就算子类的构造函数没有写这条语句,都是默认存在该语句的。
class Father {
public Father() {
System.out.println("Father 构造方法");
}
}
class Son extends Father {
public Son() {
super();
System.out.println("Son 构造方法");
}
}
public class TestDemo {
public static void main(String[] args) {
Son s = new Son();
}
}
该代码中Son的构造函数里面的super()方法无论注释与否,在new子类对象的时候,该语句默认存在于每一个子类构造函数的第一行。控制台输出的顺序先是Father 构造方法,然后Son 构造方法。那么这个时候有个问题来了,如果父类中没有构造方法怎么办呢?方法(1)在父类中加一个无参的构造方法。方法(2)子类通过super去显式调用父类的其他构造方法。方法(3)子类通过this去调用本类的其他构造方法。
下面是一个很经典的例子,由于继承的出现,我们除了考虑一个类的静态代码块,构造代码块,构造函数外,同样父类的这一套我们也都要考虑在内。具体例子如下所示:
class Father {
static {
System.out.println("Father 静态代码块");
}
{
System.out.println("Father 构造代码块");
}
public Father() {
System.out.println("Father 构造方法");
}
public Father(String s) {
System.out.println("Father 带参构造方法");
}
}
class Son extends Father {
static {
System.out.println("Son 静态代码块");
}
{
System.out.println("Son 构造代码块");
}
public Son() {
// super("Is-Me-HL");
System.out.println("Son 构造方法");
}
public Son(String s) {
//super("Is-Me-HL");
System.out.println("Son 带参构造方法");
}
}
public class TestDemo {
public static void main(String[] args) {
Son s = new Son("da");
}
}
执行结果:
Father 静态代码块
Son 静态代码块
Father 构造代码块
Father 带参构造方法
Son 构造代码块
Son 带参构造方法
从执行结果可以知道,静态代码块(父在子前)是被最快执行的,然后是父类的构造代码块、父类的构造函数,最后才是子类的这两者。(ps:小白认为:这些东西没有必要死记硬背,写一遍代码就全部记住了,如果会忘记的话,即便是现场写这么个小例子也是ok的,很快就能知道这样的一个顺序)。
(11)方法重写(Override)和方法重载(Overload)的区别?方法重载能改变返回值类型吗?
方法重写:在子类中,出现和父类一模一样的方法申明的现象。
方法重载:同一个类中,出现的方法名相同参数列表不同的现象。
方法重载可以改变返回值。重载只看参数个数和类型,不看返回值。
(12)final关键字:最终的,可以修饰类、方法和变量。特点如下:
A:修饰的类不能被继承
B:修饰的方法不能被重写
C:修饰的变量是个常量
(13)多态:同一个对象在不同时刻体现出来的不同状态。
多态的前提:A:有继承或者实现的关系。B:有方法重写。C:父类的引用指向子类对象。
下面分别以类的继承和接口的实现为例看看多态:
class Father {
public void show() {
System.out.println("Father show");
}
}
class Son extends Father {
public void show() {
System.out.println("Son show");
}
}
public class TestDemo {
public static void main(String[] args) {
Father f = new Son();
f.show();
}
}
interface Father {
public void show() ;
}
class Son implements Father {
public void show() {
System.out.println("Son show");
}
}
public class TestDemo {
public static void main(String[] args) {
Father f = new Son();
f.show();
}
}
多态的成员变量访问特点:Father f=new Son();
A:成员变量:编译看左边,运行看左边。(编译:看等号左边的父对象或者父接口中是否有该变量,运行:访问的是左边父对象的变量);
B:构造方法:创建子类对象的时候,访问父类的构造方法,对父类的数据进行初始化。
C:成员方法:编译看左边,运行看右边(编译:看等号左边的对象是否有这个方法,运行的时候运行等号右边子类的方法,这里需要注意的是:也是多态的弊端,就是如果子类重写且重载了该父类的方法,重写的方法父类对象可以调用到,但是重载的方法,父类对象时无法调用到的,也就是说多态不能使用子类的特有方法功能。此时要使用的话就只有两种方法:创建子类对象调用,或者是通过向下转型 Son s=(Son)f);
D:静态方法:编译看左边,运行看左边(静态和final方法是静态绑定的,而普通的方法,都是采用的动态绑定,向上转型就是一个体现);
(14)抽象类的特点:
A:抽象类和抽象方法必须用abstract关键字修饰;
B:抽象类中不一定有抽象方法,但是有抽象方法的必须定义为抽象类;
C:抽象类不能实例化,因为它不是具体的,抽象类有构造方法,但他不能实例化?构造方法的作用是什么呢?用于子类访问父类数据的初始化;
D:抽象类的子类:如果不想写抽象方法,该子类是一个抽象类,重写所有的抽象方法,这个时候子类是一个具体的类。
抽象类不通过本身实例化,但是可以通过多态的方式实例化,使用具体的子类(实现了抽象类的所有抽象方法)通过向上转型实现实例化。抽象类的成员特点:
成员变量:既可以是变量也可以是常量;
构造方法:有,用于子类访问父类数据的初始化;
成员方法:既可以是抽象的,也可以是非抽象的。
抽象类的成员方法特性:
A:抽象方法 强制要求子类做的事情。
B:非抽象方法 子类继承的事情,提高代码复用性。
一个类如果没有抽象方法,可以定义为抽象类吗?如果可以,有什么意义?可以,意义是不让创建对象。另外你也可以直接把某个类的函数的构造方法权限设为private,同样无法进行实例化。
abstract关键字不能和哪些关键字共存?
private:冲突,一个表示私有,一个要求子类重写。
final:冲突,一个要求不变的,一个要求重写
static:无意义,abstract的方法只是个申明,和抽象类进行一块加载,没有意义。
(15)接口的特点:
A:接口用关键字interface表示;interface 接口名 {}
B:类实现接口用implements表示;class 类名 implements 接口名{}
C:接口不能实例化;那么接口该如何实例化呢?通过多态的方式实例化
D:接口的子类;a:可以是抽象类,但是意义不大;b:可以是具体类
接口的成员特点:
接口中的变量默认是final常量并且是static的,只能初始化一次,不能重新赋值:public final static int x=100;
构造方法:接口中没有构造方法。
成员方法:接口中的方法只能是抽象方法,默认修饰符public abstract
类与类:继承关系,只能是单继承,可以多层继承。
类和接口:实现关系,可以是单实现,也可以是多实现。并且还可以在继承一个类的同时实现多个接口。
接口与接口:继承关系 可以是单继承的,也可以是多继承的。
interface A{}
interface C{}
interface B extends A,C{}
class AA{}
class BB{}
public class Test extends AA implements A,B,C {}
注意:所有的类都默认继承自一个类:Object,没写都是默认的。
(16)抽象类和接口的区别:
A:成员区别:
抽象类:成员变量:可以是变量,也可以是常量。构造方法:有。成员方法:可以是抽象,也可以非抽象。
接口:成员变量:只能是常量。构造方法:无。成员方法:只可以是抽象。
B:关系区别
类与类:继承,单继承
类与接口:实现,单实现和多实现
接口与接口:继承,单继承、多继承
C:设计理念的区别
抽象类是对类抽象,接口是对行为抽象。类包含了属性与行为,所以说接口是更具体的抽象。
抽象类是一种自下而上的设计,先有子类才能提取公同的属性与行为,抽象出父类;
接口是一种自上而下的设计,先规定行为方法,只要可以实现这些行为,就可以成为接口的实现类。
抽象类与其派生类是一种“is-a”关系,即父类和派生子类在概念上的本质是相同的(父子关系,血缘联系,很亲密)。
接口与其实现类是一种“like-a”关系,即接口与实现类的关系只是实现了定义的行为,并无本质上的联系(契约关系,很淡漠的利益关系)。
举个例子:比如说一个动物抽象类,定义了跑的方法、叫的方法,但如果一个汽车类可以实现跑、可以实现叫,它就可以继承动物抽象类吗?!这太不合理了,汽车不是动物呀!而如果通过接口定义跑的方法、叫的方法,汽车类作为实现类实现跑和叫,完全OK很合理,就因为没有继承关系的约束。
最后:有个小提醒:当方法的参数是一个抽象类或者是接口时,实际上我们要传递的是该抽象类或该接口的实现类对象。
(17)内部类:吧类定义在其他类内部,这个类就被称为内部类。
内部类的特点:
A:内部类可以直接访问外部类成员,包括私有。
B:外部类访问内部类成员,必须创建对象。
public class Test {
private String username = "Is-Me-HL";
private String password = "XiaoGeGe";
private int a=10;
public Test() {
System.out.println("Test 构造函数");
}
class Inner{
int a = 20;
public Inner() {
System.out.println("Inner:" + username + "\t" + password);
System.out.println(Test.this.a);
}
}
public void show() {
System.out.println(new Inner().a);
}
public void show2() {
System.out.println("Test:" + username + "\t" + password);
}
public static void main(String[] args) {
Test.Inner in = new Test().new Inner();
Test t = new Test();
t.show2();
}
}
//执行结果
Test 构造函数
Inner:Is-Me-HL XiaoGeGe
10
Test 构造函数
Test:Is-Me-HL XiaoGeGe
内部类的位置:
成员位置:在成员位置定义的类,被称为成员内部类。
局部位置:在局部位置定义的类,被称为局部内部类。
如何直接访问内部类的成员:外部类名.内部类名 对象名=外部类对象.内部类对象
Test.Inner in=new Test().new Inner();
1、内部类和外部类没有继承关系(内部类连外部类的私有成员都能访问,还继承啥??)。2、通过外部类名限定this对象(什么意思呢?就是内部类里面也能有this这个关键词,this.a是指向本类对象中的a成员变量,Test.this.a指向的是外部类中的a成员变量,如果想要做测试的话,只需修改上面的例子中的内部类输出语句,去掉Test,就可以很好地理解通过外部类名限定this对象这句话)。
上面的代码展示的是成员内部类,下面该代码展示时局部内部类,在该代码下面有两点要注意的:
public class Test {
private String username = "Is-Me-HL";
private String password = "XiaoGeGe";
private int a=10;
public Test() {
System.out.println("Test 构造函数");
}
public void show() {
class Inner{
int a = 20;
public Inner() {
System.out.println("Inner:" + username + "\t" + password);
System.out.println(Test.this.a);
}
}
System.out.println(new Inner().a);
}
public void show2() {
System.out.println("Test:" + username + "\t" + password);
}
public static void main(String[] args) {
Test t = new Test();
t.show();
}
}
//执行结果
Test 构造函数
Inner:Is-Me-HL XiaoGeGe
10
20
局部内部类:A;可以直接访问外部类的成员。B:在局部位置里可以创建内部类对象,通过对象调用内部类方法,来使用局部内部类功能。当局部内部类访问局部变量的时候:必须被final修饰,为什么?局部变量随着方法的调用而消失,而堆内存的内容并不会立即消失,因此加final修饰,这个变量就变成了常量,既然是常量,你消失了,我在内存中存储的是数据该值,所以,我还是有数据在使用。
(18)匿名内部类:是一个集成了该类或者实现了该接口的子类匿名对象。
interface A{
public void show ();
}
class B implements A{
@Override
public void show() {
new A() {
@Override
public void show() {
// TODO Auto-generated method stub
}
};
}
}
实际上匿名内部类是相当于实现了该接口的一个类的对象。在安卓开发中常用到匿名内部类,好处是使用完就释放掉占用的内存。在移动端容量有限的情况下,能很好地优化整个项目。
注:以上文章仅是个人学习过程总结,若有不当之处,望不吝赐教。