类
一、类与对象
类:对一类事物的描述,是抽象的、概念上的定义 ;
对象:是实际存在的该类事物的每个个体,因而也称为实例(instance);
【代码例子】
Person p1 = new Person();
Person p2 = new Person();
Person p3 = p1;//没有新创建一个对象,共用一个堆空间中的对象实体。
【匿名对象】
我们创建的对象,没显式的赋给一个变量名。即为匿名对象;匿名对象只能调用一次;
class Test{
public static void main(String[] args){
Test test = new Test();
//匿名对象
test.test(new One());
}
public void test(One one){
//操作语句
}
}
class one{}
二、类的属性与局部变量
1. 相同点
- 定义变量的格式:数据类型 变量名 = 变量值;
- 先声明,后使用;
- 变量作用在其对应的作用域;
2. 不同点
2.1类中声明的位置
- 属性:直接定义在类的一对{}内;
- 局部变量:声明在方法内、方法形参、代码块内、构造器形参、构造器内部的变量;
2.2 权限修饰符
- 属性:可以在声明属性时,指明其权限,使用权限修饰符;
- 常用的权限修饰符:private、public、缺省、protected ;
- 局部变量:不可以使用权限修饰符;
2.3 默认初始化值
- 属性:类的属性,根据其类型,都默认初始化值。
- 整型(byte、short、int、long:0);
- 浮点型(float、double:0.0);
- 字符型(char:0 (或’\u0000’));
- 布尔型(boolean:false);
- 引用数据类型(类、数组、接口:null);
- 局部变量:没默认初始化值;
- 意味着,我们在调用局部变量之前,一定要显式赋值;
- 特别地:形参在调用时,我们赋值即可;
2.4 在内存中加载的位置
-
属性:加载到堆空间中 (非static);
-
局部变量:加载到栈空间;
三、方法
1、什么是方法
-
一个方法只完成一个功能
-
格式:
- 可以不用参数
- [修饰符] 返回值类型 方法名(参数类型 参数名){ 方法体 }
- return 表示结束方法,跳出这个方法体
【注意:参数是方法中才有的说法,调用方式时要传入的叫实参,声明方法中的是形参,形参要声明数据类型】
2、返回值类型
如果方法有返回值,则必须在方法声明时,指定返回值的类型;
- return关键字来返回指定类型的变量或常量:“return 数据”;
- 如果方法没返回值,则方法声明时,使用void来表示。通常,没返回值的方法中,就不需要;
3、值传递机制
如果变量是基本数据类型,此时赋值的是变量所保存的数据值。 如果变量是引用数据类型,此时赋值的是变量所保存的数据的地址值;
【代码例子】
System.out.println("***********基本数据类型:****************");
int m = 10;
int n = m;
System.out.println("m = " + m + ", n = " + n);
n = 20;
System.out.println("m = " + m + ", n = " + n);
System.out.println("***********引用数据类型:****************");
Order o1 = new Order();
o1.orderId = 1001;
Order o2 = o1;//赋值以后,o1和o2的地址值相同,都指向了堆空间中同一个对象实体。
System.out.println("o1.orderId = " + o1.orderId + ",o2.orderId = " +o2.orderId);
o2.orderId = 1002;
System.out.println("o1.orderId = " + o1.orderId + ",o2.orderId = " +o2.orderId);
4、重载
定义:在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同即可;
- 方法名称必须相同
- 参数列表不同,个数不同、或者类型不同、或者参数排列顺序不同
- 返回值类型可同可不同
- 仅仅返回值不同不足成为方法的重载
【代码例子】
public void getSum(int i,int j){
System.out.println("1");
}
public void getSum(double d1,double d2){
System.out.println("2");
}
public void getSum(String s ,int i){
System.out.println("3");
}
public void getSum(int i,String s){
System.out.println("4");
}
5、重写
子类继承父类以后,可以对父类中同名同参数的方法,进行覆盖操作;
重写以后,当创建子类对象以后,通过子类对象调用子父类中的同名同参数的方法时,实际执行的是子类重写父类的方法;
【方法的声明】
权限修饰符 返回值类型 方法名(形参列表) throws 异常的类型{
//方法体
}
【代码例子】
class Circle{
public double findArea(){}//求面积
}
class Cylinder extends Circle{
public double findArea(){}//求表面积
}
class Account{
public boolean withdraw(double amt){}
}
class CheckAccount extends Account{
public boolean withdraw(double amt){}
}
【重写规则】
-
子类重写的方法的方法名和形参列表与父类被重写的方法的方法名和形参列表相同;
-
子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符;
-
子类不能重写父类中声明为private权限的方法;
-
返回值类型:
-
父类被重写的方法的返回值类型是void,则子类重写的方法的返回值类型只能是void;
-
父类被重写的方法的返回值类型是A类型,则子类重写的方法的返回值类型可以是A类或A类的子类;
-
父类被重写的方法的返回值类型是基本数据类型(比如:double),则子类重写的方法的返回值类型必须是相同的基本数据类型(必须也是double);
-
-
子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型(具体放到异常处理时候讲)
子类和父类中的同名同参数的方法要么都声明为非static的(考虑重写,要么都声明为static的(不是重写);
6、命令行传参
在命令行中执行类;
-
在命令行中使用命令:javac 全类名 [参数1] [参数2] [参数3]
-
注意是全类名,要在包名外执行命令行;
-
相当与给 main方法中的 参数 String[] args ,传值,放在这个 String[] 数组中;
7、可变参数
- 不定项参数
- 在方法声明中,在指定参数类型后加一个省略号(…)
- 一个方法中只能指定一个可变参数,它必须是方法的最后一个参数,任何普通的参数必须在它之前声明;
public static void main(String[] args) {
//可以传任意个参数
add(1,2,5,3,5,6,5);
}
//类方法
public static void add(double... a){
System.out.println(a[0]);
System.out.println(a[1]);
}
//输出(相当与 参数 a 变成了一个数组)
1.0
2.0
8、递归
- 递归头:什么时候不调用自身方法,如果没有头,将陷入死循环
- 递归体:什么时候需要调用自身方法。
- java是栈机制,每运行一个方法就会上主方法上压一个方法,执行后一个就消失一个,直到执行完main方法,栈就空了;
- 栈不是无限的,递归无疑是增加了jvm的开销;
- 基数小才能用递归;
public static void main(String[] args) {
System.out.println(f(5));
}
//递归函数
public static int f(int n){
if(n == 1){
return 1;
}else{
return n*=f(n-1);
}
}
边界条件:边界
前阶段
返回阶段
9、方法的调用
- 被static修饰符修饰的方法是类方法,不用实例化类,可以直接用类名调用。
- 被static修饰的方法,不能调用不是被static修饰的方法;
- 被static修饰的方法,是和类一起加载的;
- 而没有被static修饰的方法,是需要类实例化之后才存在的;
- 就是说static方法不能调用不存在的方法(未实例化);
- 反之,被static修饰的方法只能调用被static修饰的方法;
四、类的构造器
只要造对象就得用构造器;
构造器的作用:创建对象,初始化对象的信息;
1、说明
- 如果没显式的定义类的构造器的话,则系统默认提供一个空参的构造器;
- 定义构造器的格式:权限修饰符 类名(形参列表){};
- 一个类中定义的多个构造器,彼此构成重载;
- 一旦我们显式的定义了类的构造器之后,系统就不再提供默认的空参构造器;
- 一个类中,至少会有一个构造器;
- 构造器默认权限和类的权限一致;
【代码例子】
//构造器不等于方法
//无参
public Person(){
System.out.println("Person().....");
}
//一个参数
public Person(String n){
name = n;
}
//多个参数
public Person(String n,int a){
name = n;
age = a;
}
2、属性赋值顺序
① 默认初始化
② 显式初始化
③ 构造器中初始化
④ 通过"对象.方法" 或 "对象.属性"的方式,赋值
以上操作的先后顺序:① - ② - ③ - ④
3、JavaBean的概念
所谓JavaBean,是指符合如下标准的Java类:
-
类是公共的;
-
一个无参的公共的构造器;
-
属性,且有对应的get、set方法;
五、代码块
代码块(初始化块),用来初始化类、对象的信息;重要性较属性、方法、构造器差一些;
【注意】
代码块要是使用修饰符,只能使用static ;
1、静态代码块与非静态代码块
静态代码块:
- 内部可以输出语句;
- 随着类的加载而执行,而且只执行一次;
- 作用:初始化类的信息;
- 如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行;
- 静态代码块的执行要优先于非静态代码块的执行;
- 静态代码块内只能调用静态的属性、静态的方法,不能调用非静态的结构;
非静态代码块
- 内部可以输出语句;
- 随着对象的创建而执行;
- 每创建一个对象,就执行一次非静态代码块;
- 作用:可以在创建对象时,对对象的属性等进行初始化;
- 如果一个类中定义了多个非静态代码块,则按照声明的先后顺序执行;
- 非静态代码块内可以调用静态的属性、静态的方法,或非静态的属性、非静态的方法;
【注意】
实例化子类对象时,涉及到父类、子类中静态代码块、非静态代码块、构造器的加载顺序:由父及子,静态先行;
2、属性的赋值顺序
①默认初始化;
②显式初始化/⑤在代码块中赋值;
③构造器中初始化;
④有了对象以后,可以通过"对象.属性"或"对象.方法"的方式,进行赋值;
执行的先后顺序:① - ② / ⑤ - ③ - ④
六、内部类
Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B称为外部类;
内部类的类别
成员内部类(静态、非静态) ;局部内部类(方法内、代码块内、构造器内);
-
成员内部类
-
可以把它当作是类的一个成员,相当于实例变量;
Outer outer = new outer(); //通过外部类的实例来实例化内部类 Outer.Inner inner = outer.new Inner();
-
可以操作外部类的私有成员;
-
-
静态内部类
- 实际上也是一个类成员,用static修饰的内部类;
- 和类一起加载,用类名调用;
- 不能调用实例变量,或者非静态的成员;
//创建静态的Dog内部类的实例(静态的成员内部类): Person.Dog dog = new Person.Dog(); //创建非静态的Bird内部类的实例(非静态的成员内部类): //Person.Bird bird = new Person.Bird();//错误的 Person p = new Person(); Person.Bird bird = p.new Bird(); //需要对象调用 p.new xx();
-
局部内部类
- 就是一个方法里定义的类,和局部变量一般;
-
匿名内部类(匿名子类)
- 没有名字初始化类,不用将实例保存到变量中(匿名对象);
String name = new Person().name; //这个实现类是匿名的,匿名实现接口; new PersonInterface(){ @Override public void run() { } @Override public int add() { return 0; } };
【注意】
-
成员内部类和局部内部类,在编译以后,都会生成字节码文件。
- 格式:
- 成员内部类:外部类$内部类名.class ;
- 局部内部类:外部类$数字 内部类名.class;
- 格式:
-
类内可以定义属性、方法、构造器等;
-
可以被final修饰,表示此类不能被继承。言外之意,不使用final,就可以被继承;
-
可以被abstract修饰;
-
一个java文件里只能有一个public修饰的类,可以有多个class类(当然接口也可以),这写class类是定义在public修饰的类的外边,但是同属一个java文件;
-
而内部类是定义在类的里边;
七、抽象类
abstract 可以修饰类和方法,表示抽象类或者抽象方法;
- 打开一个抽象类的class文件,可以看到它其实也是有默认的构造方法的;
public abstract class AbstractClass {
public AbstractClass() {
}
public abstract void doSomething(); //抽象方法
}
【注意】
- 抽象类不能实例化,只能靠子类去实例化,如果子类还是抽象类,自然也不能实例化;
- 抽象类中可以写普通的方法;
- 抽象方法必须在抽象类中;
- 非抽象类的子类继承抽象类,需要重写所有抽象方法;
- 可以说抽象方法是一种规范和约束;
接口
【interface关键字】
- 接口使用interface来定义
- Java中,接口和类是并列的两个结构
一、interface的使用
-
如何定义接口:定义接口中的成员;
-
接口中不能定义构造器的!意味着接口不可以实例化;
-
Java开发中,接口通过让类去实现(implements)的方式来使用:
- 如果实现类覆盖了接口中的所抽象方法,则此实现类就可以实例化;
- 如果实现类没覆盖接口中所的抽象方法,则此实现类仍为一个抽象类;
//抽象类实现接口不需要重写接口里的方法; public abstract class AbstractClass implements PersonInterface{
-
Java类可以实现多个接口 → 弥补了Java单继承性的局限性;
格式:class AA extends BB implements CC,DD,EE;
-
接口与接口之间可以继承,而且可以多继承;
-
接口的具体使用,体现多态性;
-
接口,实际上可以看做是一种规范;
【代码例子】
public interface PersonInterface {
//默认都是public static final
int NUM = 10;
//默认都是public abstract
void run();
int add();
}
- 接口需要类来实现(implements),实现类一般命名为 xxxImpl;
public class DemoInterfaceImpl implements DemoInterface {
}
二、Java8中关于接口的新规范
- JDK7及以前:只能定义全局常量和抽象方法
- 全局常量:public static final,书写时可以省略不写;
- 抽象方法:public abstract;
- JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法;
- 静态方法:使用 static关键字修饰;可以通过接口直接调用静态方法,并执行其方法体;
- 默认方法:默认方法使用 default关键字修饰;可以通过实现类对象来调用;
【Java8后的接口】
- 接口中定义的静态方法,只能通过接口来调用;
- 通过实现类的对象,可以调用接口中的默认方法。如果实现类重写了接口中的默认方法,调用时,仍然调用的是重写以后的方法;
- 如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的默认方法,那么子类在没重写此方法的情况下,默认调用的是父类中的同名同参数的方法。(类优先原则);
- 如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,那么在实现类没重写此方法的情况下,报错。接口冲突。这就需要我们必须在实现类中重写此方法;
- 如何在子类(或实现类)的方法中调用父类、接口中被重写的方法;
public void myMethod(){
method3();//调用自己定义的重写的方法
super.method3();//调用的是父类中声明的
//调用接口中的默认方法
CompareA.super.method3();
CompareB.super.method3();
}
【抽象类和接口的异同】
-
相同点:
- 都不能实例化;都可以被继承;都可以包含抽象方法的。
-
不同点:
-
抽象类:有构造器;接口:没有构造器;
-
类:单继承性; 接口:多继承;
-
类与接口:多实现;
-