目录
类
对象
类与对象的关系
对象的创建
对象的内存解析
对象数组的内存解析
匿名对象
面向对象与面向过程的理解
类的成员
属性
属性与局部变量的对比
属性的默认初始化值
方法
方法的声明
方法的重载
方法的重写(override 或 overwrite)
重写和重载的区别
构造器(或构造方法)Constructor
构造器的作用
属性赋值的顺序
JavaBean的概念
代码块
静态代码块
非静态代码块
加载顺序
属性赋值先后顺序:
内部类
成员内部类(静态、非静态)
局部内部类
JVM内存结构
对一类事物的描述,是抽象的和概念上的定义。
是实际存在的该类事物的每个个体,因此也称为实例(instance)。面向对象程序设计的重点就是类的设计,而设计类,主要就是设计类的成员。
对象是由类new出来的,或者说派生出来的。
class Student{
// ......
}
Student s1 = new Student();
Student s2 = new Student();
Student s3 = s2; //没有新创建一个对象,s3和s2共用一个堆空间中的对象实体
创建对象时没有显式地赋给一个变量名,即为匿名对象,特点是匿名对象只能调用一次。
class Person{
double money;
public void playGame(){
//......
}
public void shopping(){
//......
}
}
class Mall{
public void show(Person p){
p.shopping();
}
}
//匿名对象
new Person().money = 88888888;
new Person().playGame();
//匿名对象的使用
Mall m = new Mall();
m.show(new Person());
面向过程:主要强调的是功能行为,以函数为最小单位,考虑怎么做。
面向对象:主要强调具备了功能的对象,以类或对象为最小的单位,考虑谁来做。
属性、方法、构造器、代码块、内部类。
补充说明: 属性 = 成员变量 = 域、字段 = field
方法 = 成员方法 = 函数 = method
创建类的对象 = 类的实例化 = 实例化类
相同点:
1、定义变量的格式:DataType VariableName = VariableValue
2、先声明,后使用。
3、变量都有其对应的作用域。
不同点:
1、在类中声明的位置不同。
属性定义在类的一对{}内;局部变量声明在方法内、方法形参、代码块内、构造器形参、构造器内部的变量。
2、权限修饰符不同
属性在声明时可以指明其权限,例如:public ,private, 缺省, protected;而局部变量不可以使用权限修饰符。
3、默认初始化值
4、在内存中加载的位置不同
属性(非static):加载到堆空间中。
局部变量:加载到栈空间中。
整型 (byte、short、int、long):0
浮点型 (float、double):0.0
字符型 (char):0或'\u0000'
布尔型 (boolean):false
引用数据类型 (类、数组、接口):null
局部变量没有默认初始化值。
描述类应该具有的功能。例如:Arrays类中的sort()、binarySearch()、toString()、equals()...
权限修饰符 返回值类型 方法名(形参列表){ //方法体 }
注意:
有返回值:必须在声明方法时指定返回值的类型,同时在方法体中使用return关键字返回指定类型的变量或者常量:“return 数据”。
无返回值:声明方法时使用void来表示,通常没有返回值的方法中不需要使用return,但如果使用的话,只能“return;” ,表示结束此方法。
return关键字后面不可以再声明执行语句。
在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或参数类型不同即可。
与方法的权限修饰符、返回值类型、形参变量名、方法体都没有关系!!!
//方法的重载
//同一个类且相同的方法名;参数列表不同(参数个数,参数类型不同),严格按此定义判断
//以下的4个方法构成了重载
public void getNum(int i,int j){
System.out.println("1");
}
public void getNum(double i,double j){
System.out.println("2");
}
public void getNum(String s,int i){
System.out.println("3");
}
public void getNum(int i,String s){
System.out.println("4");
}
//这个不能与上面的方法构成重载
public int getNum(int i,int j){
return 0;
}
子类继承父类后,可以对父类中的同名同参数的方法进行覆盖操作。
应用:
重写以后,当创建子类对象以后,通过子类对象调用子父类中的同名同参数的方法时,实际执行的是子类重写父类的方法。
class Circle{
public double findArea(){}//求面积
}
class Cylinder extends Circle{
public double findArea(){}//求表面积
}
重写的规则:
方法的声明: 权限修饰符 返回值类型 方法名(形参列表) throws 异常的类型{
//方法体
}
约定俗称:子类中的叫重写的方法,父类中的叫被重写的方法。
① 子类重写的方法的方法名和形参列表与父类被重写的方法的方法名和形参列表相同。
② 子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符。
特殊情况:子类不能重写父类中被声明为private权限的方法。
③ 返回值类型:
父类被重写的方法的返回值类型是void,则子类重写的方法的返回值类型只能是void。
父类被重写的方法的返回值类型是A类型,则子类重写的方法的返回值类型可以是A类或A类的子类。
父类被重写的方法的返回值类型是基本数据类型(比如:double),则子类重写的方法的返回值类型必须是相同的基本数据类型(即,必须也是double)。
④子类重写的方法所抛出的异常类型不大于父类被重写的方法抛出的异常类型。
子类和父类中的同名同参数的方法要么都声明为非static的(考虑重写),要么都声明为static的(不是重写)。
①概念不同、具体规则不同。(参考上面所写的)
②重载:不表现为多态性。
重写:表现为多态性。
重载:是指允许存在多个同名方法,而这些方法的参数不同。编译器根据方法不同的参数表,对同名方法的名称做修饰。对于编译器而言,这些同名方法就成了不同的方法。它们的调用地址在编译期就绑定了。Java的重载是可以包括父类和子类的,即子类可以重载父类的同名不同参数的方法。
所以对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法,这称为“早绑定”或“静态绑定”;
而对于多态:只等到方法调用的那一刻,解释运行器才会确定所要调用的具体方法,这称为“晚绑定”或“动态绑定”。
Bruce Eckel说过:“不要犯傻,如果它不是晚绑定,它就不是多态。”
1.创建对象。
2.初始化对象的信息。
说明
1.如果没显式的定义类的构造器的话,则系统默认提供一个空参的构造器。
2.定义构造器的格式: 权限修饰符 类名(形参列表){}
3.一个类中定义的多个构造器,彼此构成重载。
4.一旦我们显式的定义了类的构造器之后,系统就不再提供默认的空参构造器
5.一个类中,至少会有一个构造器。
class Person{
String name;
int age;
//构造器
public Person(){
System.out.println("666666");
}
public Person(String n){
name = n;
}
public Person(String n,int a){
name = n;
age = a;
}
}
① 默认初始化。
② 显式初始化。
③ 构造器中初始化。
④ 通过"对象.方法" 或 "对象.属性"的方式赋值。
1、类是公共的。
2、有一个无参的公共的构造器。
3、有属性,且有对应的get()、set()方法。符合这三点的就是JavaBean。
Java中用{}括起来的内容称为代码块。代码块的作用是用来初始化类、对象的信息。没有使用修饰符的代码块叫非静态代码块;要是使用修饰符的话只能使用static,此时叫静态代码块。
1、内部可以输出语句。
2、随着类的加载而执行,而且只执行一次,并且在main方法之前执行。
3、作用是初始化类的信息。
4、如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行。
5、静态代码块的执行要优先于非静态代码块的执行。
6、静态代码块内只能调用静态的属性、静态的方法,不能调用非静态的结构。
//格式
static {
//......
}
//静态代码块在类加载时执行,并且在main方法执行前执行,且只执行一次
public class Test {
public static void main(String[] args) {
//输出main方法执行时当前时间
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
System.out.println("main方法执行时当前时间"+sdf.format(new Date()));
}
//静态代码块
static {
//输出静态代码块执行时当前时间
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
System.out.println("静态代码块执行时当前时间"+sdf.format(new Date()));
}
}
1、内部可以输出语句。
2、随着对象的创建而执行。
3、每创建一个对象,就执行一次非静态代码块。
4、作用:可以在创建对象时,对对象的属性等进行初始化。
5、如果一个类中定义了多个非静态代码块,则按照声明的先后顺序执行。
6、非静态代码块内可以调用静态的属性、静态的方法,或非静态的属性、非静态的方法。
实例化子类对象时,如果涉及到父类、子类中静态代码块、非静态代码块、构造器,它们的加载顺序用一句话总结:由父及子,静态先行,非静态代码块优先构造器。
public class Test {
public static void main(String[] args) {
new Son();
}
}
class Father{
public Father() {
System.out.println("父类构造器");
}
{
System.out.println("父类非静态代码块");
}
static {
System.out.println("父类静态代码块");
}
}
class Son extends Father{
{
System.out.println("子类非静态代码块");
}
public Son() {
System.out.println("子类构造器");
}
static {
System.out.println("子类静态代码块");
}
}
上面代码运行结果是:
父类静态代码块
子类静态代码块
父类非静态代码块
父类构造器
子类非静态代码块
子类构造器
①默认初始化
②显示初始化或代码块中赋值
③构造器中初始化
④通过“对象.属性”或“对象.方法”
Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B称为外部类。
怎样理解:
一方面,作为外部类的成员:
可以调用外部类的结构。
可以被static修饰。
可以被4种不同的权限修饰。
另一方面,作为一个类:
类内可以定义属性、方法、构造器等。
可以被final修饰,表示此类不能被继承。言外之意,不使用final,就可以被继承
可以被abstract修饰。
public class InnerClassTest {
public static void main(String[] args) {
//创建Dog实例(静态的成员内部类):
Person.Dog dog = new Person.Dog();
dog.show();
//创建Bird实例(非静态的成员内部类):
//Person.Bird bird = new Person.Bird();//错误的
Person p = new Person();
Person.Bird bird = p.new Bird();
bird.sing();
System.out.println();
bird.display("黄鹂");
}
}
class Person{
String name = "小明";
int age;
public void eat(){
System.out.println("人:吃饭");
}
//静态成员内部类
static class Dog{
String name;
int age;
public void show(){
System.out.println("卡拉是条狗");
//eat();
}
}
//非静态成员内部类
class Bird{
String name = "鹦鹉";
public void display(String name){
System.out.println(name);//方法的形参
System.out.println(this.name);//内部类的属性
System.out.println(Person.this.name);//外部类的属性
//Person.this.eat();
}
}
}
public void method(){
//局部内部类
class AA{
}
}
{
//局部内部类
class BB{
}
}
public Person(){
//局部内部类
class CC{
}
}
在局部内部类的方法中,如果调用局部内部类所声明的方法中的局部变量的话,要求此局部变量声明为final的。
jdk 7及之前版本:要求此局部变量显式的声明为final的。
jdk 8及之后的版本:可以省略final的声明。
总结:
成员内部类和局部内部类,在编译以后都会生成字节码文件。
格式:
成员内部类:外部类$内部类名.class
局部内部类:外部类$数字 内部类名.class
编译完源程序以后,生成一个或多个字节码文件,然后使用JVM中的类的加载器和解释器对生成的字节码文件进行解释运行。
堆(Heap):
此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存 。 这一点在Java 虚拟机规范中的描述是:所有的对象实例以及数组都要在堆上分配 。
栈(Stack):
是指虚拟机栈 。虚拟机栈用于存储局部变量等 。局部变量表存放了编译期可知长度的各种基本数据类型(boolean 、 byte 、char 、 short 、 int 、 float 、 long 、double )、 对象引用(reference 类型,它不等同于对象本身,是对象在堆内存的首地址)。 方法执行完,自动释放 。
方法区(Method Area):
用于 存储已被虚拟机加载的类信息 、 常量 、 静态变量 、 即时编译器编译后的代码等数据 。