一个对象的产生至少有以下两步:
1.为对象分配内存
2.调用合适的构造方法
java是一门纯面向对象的语言(Object Oriented Program)简称OOP,在面向对象的世界里,一切皆为对象。面向对象是解决问题的一种思想,主要依靠对象之间的交互完成一件事情。用面向对象的思想来设计程序,更符合人们对事物的认知,对于大型程序的设计,扩展以及维护都非常友好。
什么是面向过程?
拿传统洗衣服举例:我们要
拿盆子–放水–放衣服–放洗衣粉–手搓–换水–放洗衣粉–手搓–拧干–晾衣服
传统的方式,注重的是洗衣服的过程,少了一个环节都不行
现代洗衣服
总共有四个对象:人,衣服,洗衣粉,洗衣机
整个洗衣服的过程:人将衣服放进洗衣机,倒入洗衣粉,启动洗衣机,洗衣机就会完成洗衣服并且甩干
这四个对象协作完成洗衣服这件事情
再比如说
你刚上班的时候,想买个手机,这个时候没人帮你买,你就要自己去上网查手机参数–打开电商平台–找到这个手机–下单–写地址–取快递–拆快递–验货(这个是面向过程)
等后来你成为了大老板,有了一个女秘书,这个时候你想买个手机,你只需要告诉你的女秘书,她就会帮你完成所有的步骤,你只需要跟秘书这个对象完成交接就行了(这个就是面向对象)
面向对象程序设计关注的是对象,而对象是现实生活中的实体,比如洗衣机,但是计算机并不认识洗衣机,需要开发人员告诉计算机什么是洗衣机。
这个图片就是对洗衣机的简单描述,该过程称为对洗衣机对象(实体)进行抽象(对一个复杂事物的重新认知),但是这些简化的抽象结果计算机并不能识别,开发人员可以采用某种面向对象的编程语言来进行描述,比如java
类是用来对一个实体(对象)进行描述的,主要描述该实体(对象)具有哪些属性(外观尺寸颜色等),哪些功能(用来干啥),描述完成后计算机就可以识别了
比如:洗衣机,它在java中就可以看成一个类
属性:品牌,型号,重量,尺寸,颜色…
功能:洗衣,烘干…
class ClassName//类名字采用大驼峰的形式,每个字母大写
{
field;//字段(或者叫属性,或者叫成员变量)
method;//行为或者成员方法
}
class为定义类的关键字,ClassName为类名,{}中为类的主体
每一个类对应一个字节码文件,下面的洗衣机类就会生成一个WashMachine.class的字节码文件
定义一个洗衣机类
class WashMachine
{
//字段/属性/成员属性/成员变量(定义在类里,方法外面)
public String brand;//品牌“小白”
public String type;//型号
public double weight;//重量
public double length;//长度
public double width;//宽度
public double height;//高度
public String color;//颜色
//成员方法
public void washClothes()
{
System.out.println("洗衣服");
}
public void dryClothes()
{
System.out.println("脱水");
}
public void setTime()
{
System.out.println("定时");
}
}
采用java语言将洗衣机类在计算机中定义完成,经过javac编译之后形成.class文件,在JVM的基础上计算机就可以识别了
注意:
1.类目必须采用大驼峰的形式
2.成员前写法统一为public,后面会详细解释
3.此处写的方法不带static关键字,后面会详细解释
4.方法名称都是小驼峰(第一个单词小写,后面都大写)
5.public修饰的类必须和文件名相同
6.main方法所在的类一般要用文件名修饰
定义了一个类,就相当于在计算机中定义了一个新的类型,与int,double,类似,只不过int和double是java语言自己自带的内置类型,而类是用户定义了一个新的类型,有了这些自定义类型之后,就可以使用这些类来定义实例(或者叫定义对象)
用类的类型创建对象的过程,叫做类的实例化,在java中采用new关键字,配合类名来实例化对象
class WashMachine
{
//字段/属性/成员属性/成员变量(定义在类里,方法外面)
public String brand;//品牌“小白”
public String type;//型号
public double weight;//重量
public double length;//长度
public double width;//宽度
public double height;//高度
public String color;//颜色
//成员方法
public void washClothes()
{
System.out.println("洗衣服");
}
public void dryClothes()
{
System.out.println("脱水");
}
public void setTime()
{
System.out.println("定时");
}
}
public class Class_And_Object
{
public static void main(String[] args)
{
//washMachine是一个引用变量,指向new WashMachine()这个对象,washMachine存的是new WashMachine()的地址
WashMachine washMachine=new WashMachine();
//类型WashMachine 定义了一个变量washMachine,赋了一个new WashMachine()的值
//也就是说washMachine这个对象里有WashMachine这个类里的所有属性
washMachine.brand="小白";
System.out.println(washMachine.brand);
WashMachine washMachine2=new WashMachine();
washMachine2.brand="小天鹅";
System.out.println(washMachine2.brand);
}
}
结论:可以使用new关键字实例化多个对象,多个对象之间互相不影响,因为它们都有一块独立的内存空间
class Person
{
public String name;
public int age;
public void eat()
{
System.out.println(name+"在吃饭");
}
}
public static void main(String[] args)
{
Person person1=new Person();
person1.name="张三";
person1.eat();//这个时候会输出张三在吃饭,调用eat的时候name已经被赋值了
}
1.类只是一个模型一样的东西,用来对一个实体进行描述,限定了类有哪些成员
2.类是一种自定义模型,可以用来自定义变量
3.一个类可以实例化出多个对象,实例化出的对象占用实际的物理空间,存储成员变量
4.做个比方。类实例化出对象就像现实中使用建筑设计图建造出房子,类就像是设计图,只设计出需要什么东西,但是并没有实体的建筑存在,同样类也只是一个设计,实例化出的对象才能实际存储数据,占用物理空间
先来看一个日期类
class TestDate
{
public int year;
public int month;
public int day;
public void setDate(int y,int m,int d)
{
year=y;
month=m;
day=d;
}
public void printDate()
{
System.out.println(year+"年"+month+"月"+day+"日");
}
public static void main(String[] args)
{
TestDate testDate1=new TestDate();
TestDate testDate2=new TestDate();
TestDate testDate3=new TestDate();
testDate1.setDate(2022,5,11);
testDate2.setDate(2023,6,3);
testDate3.setDate(2024,10,18);
testDate1.printDate();
testDate2.printDate();
testDate3.printDate();
}
}
问题来了:
问题1:setDay这个方法只有一个,我们赋值调用的时候怎么知道setDay调用的是对象testdata1的值,还是对象testdata2的值,还是对象testdata3的值呢
这个时候我们就要用this了
还有一个问题2:不多说,上代码
仔细观察这个代码块和上面那个代码块有什么不同
class TestDate
{
public int year;
public int month;
public int day;
public void setDate(int year,int month,int day)
{
year=year;
month=month;
day=day;
}
public void printDate()
{
System.out.println(year+"年"+month+"月"+day+"日");
}
public static void main(String[] args)
{
TestDate testDate1=new TestDate();
TestDate testDate2=new TestDate();
TestDate testDate3=new TestDate();
testDate1.setDate(2022,5,11);
testDate2.setDate(2023,6,3);
testDate3.setDate(2024,10,18);
testDate1.printDate();
testDate2.printDate();
testDate3.printDate();
}
}
这个图是这个代码块的运行结果
我们可以看到,两个代码块基本都一样,只不过第二个代码块把成员方法里面局部变量的名字都改成了跟成员变量的名字一样的了
就是上图这块
当我们把这段代码改成加上this的,也就是下图
它的运行结果就变成了
这是为什么呢
首先对于问题1的解释:
其他代码不动,只改图片内容
就这个代码来说,如果不改也对,但是加上了一定对,不改可能错,建议以后加上
我们可以看到,有三个对象被创造了出来,分别是testdata1,testdata2和testdata3,我们用哪个对象的时候,this就指代的哪个对象,这是编译器自己智能的一个体现
对于问题2:
不加this的时候,变量名字跟成员变量相同,这个时候编译器采用了局部优先原则,也就是在setDate后面的括号的变量是局部变量,因为两个都叫year,编译器这个时候看到局部有year了,就检测不到外部的成员变量year,就相当于局部变量year自己给自己赋值了,然后外部的成员变量year没被赋值,就是0
加上this之后,this.year指定了哪个对象的year要被赋值,如果调用第一个对象,就指定了第一个对象的成员变量year被赋值
this引用指向当前对象(成员方法运行时调用该成员方法的对象),在成员方法中所有成员变量的操作,都通过该引用去访问,只不过所有的操作对用户都是透明的,即用户不需要来传递,编译器自动完成
更深层次的理解就是编译器自动把这个变量隐藏了, 原本是在成员方法后面的一个参数值应该是如下两图所示类型,用来接收你传过来的调用的对象,编译器比较智能,自己隐藏了,你加不加这个(TestDate this)变量都行
1.this的类型:对应类的类型的引用,即哪个对象调用就是哪个对象的引用类型
2.this只能在成员方法中使用
3.在成员方法中,this只能引用当前对象,不能引用其他对象
4.this是成员方法第一个隐藏参数,编译器会自动传递,在成员方法执行时,编译器会负责将调用成员方法对象的引用传递给该成员方法,this负责来接受
如何初始化对象–(也就是)如何初始化对象当中的成员
在java方法内部定义一个局部变量时,必须初始化,否则会编译失效
上图就是错的,因为没给a赋值(初始化)
如果是对象,需要调用之前的setDate的方法才可以将具体的日期设置到对象中。
通过上述两个例子发现两个问题:
1.每次对象创建好之后调用setDate方法设置具体日期,比较麻烦,那对象该如何初始化呢?
2.局部变量必须要初始化才能使用,为什么成员变量声明之后没有赋值依然可以运行呢?
因此,我们引入了构造方法
构造方法,也成为构造器,是一个特殊的成员方法,名字必须跟类名相同,在创建对象时,由编译器自动调用,并且在整个对象的生命周期内只调用一次
class TestDate
{
public int year;
public int month;
public int day;
//构造方法可以重载
TestDate()
{
System.out.println("没有参数的构造方法!");
}
TestDate(int year,int month,int day)
{
this.year=year;
this.month=month;
this.day=day;
System.out.println("有三个参数的构造方法");
}
public void setDate(TestDate this,int y,int m,int d)
{
this.year=y;
this.month=m;
this.day=d;
}
public void printDate(TestDate this)
{
System.out.println(year+"年"+month+"月"+day+"日");
}
public static void main(String[] args)
{
TestDate testDate1=new TestDate();
TestDate testDate2=new TestDate();
TestDate testDate3=new TestDate();
testDate1.setDate(2022,5,11);
testDate2.setDate(2023,6,3);
testDate3.setDate(2024,10,18);
testDate1.printDate();
testDate2.printDate();
testDate3.printDate();
}
}
问题又来了,我们没写构造方法的时候也没有报错
解答:我们没有写构造方法的时候,编译器会自动给我们写一个不带任何参数的构造方法,如果我们写了构造方法,编译器就不给我们了
构造方法怎么调用呢?
就是下图
这个new TestDate()就是调用的构造方法,这个括号里面没有参数,就是调用的不带参数的构造方法,如果类似new TestDate(2022,1,30)的话,就是调用带三个参数的构造方法
class TestDate
{
public int year;
public int month;
public int day;
TestDate()
{
System.out.println("没有参数的构造方法!");
}
TestDate(int year,int month,int day)
{
this.year=year;
this.month=month;
this.day=day;
System.out.println("有三个参数的构造方法");
}
public void setDate(TestDate this,int y,int m,int d)
{
this.year=y;
this.month=m;
this.day=d;
}
public void printDate(TestDate this)
{
System.out.println(year+"年"+month+"月"+day+"日");
}
public static void main(String[] args)
{
TestDate testDate1=new TestDate(2022,5,11);
TestDate testDate2=new TestDate(2023,6,3);
TestDate testDate3=new TestDate();
//testDate1.setDate(2022,5,11);
//testDate2.setDate(2023,6,3);
//testDate3.setDate(2024,10,18);
testDate1.printDate();
testDate2.printDate();
testDate3.printDate();
}
}
假如下面两个代码块拼装起来,就错了
因为你没有构造方法的时候,编译器给你一个不带参数的构造方法,你有了,就不给你了,所以你的new后面的括号必须赋值,跟你自己所创建的构造方法相对应
如果下面两个代码块想结合,运行出来的结果就是2024.10.18
所以我们以后初始化的时候直接用构造方法就可以了,不用费事的写一个成员方法再初始化了
在没有参数的构造方法当中用this引用直接给对象的成员变量(属性)赋值也可以,上代码!
class Student
{
public int age;
public String name;
Student()
{
this.age=10;
this.name="小白";
System.out.println("没有参数的构造方法!");
}
public void print()
{
System.out.println("年龄:"+this.age+" "+"姓名:"+this.name);
}
}
public class Class_And_Object2
{
public static void main(String[] args)
{
Student student=new Student();
student.print();
}
}
那么我们如何在没有参数的构造方法里面调用有参数的构造方法呢?
上代码!
class Student
{
public int age;
public String name;
Student()
{
this(18,"小白");
System.out.println("没有参数的构造方法!");
}
Student(int age,String name)
{
this.age=age;
this.name=name;
}
public void print()
{
System.out.println("年龄:"+this.age+" "+"姓名:"+this.name);
}
}
public class Class_And_Object2
{
public static void main(String[] args)
{
Student student1=new Student();
student1.print();
Student student2=new Student();
}
}
注意:
1.this()只能在构造方法里面调用构造方法,不能把this()放在一个普通的方法里面调用构造方法!如下图
2.构造方法当中,不能调用自己,如下图
3.只能放在构造方法内部的第一行
4.不能形成环,比如在第一个构造方法里面调用第二个,再在第二个构造方法里面调用第一个。
另外,this除了可以在构造方法里面调用构造方法和this.成员变量之外,还可以在方法里面调用另外一个方法,这个this可以不用放在第一行
在IDEA中怎么让编译器自己生成构造方法呢?
首先要公开
按住Shift多选
就自动生成成功了
1.名字必须与类名相同
2.没有返回值类型,设置为void也不行
3.创建对象时由编译器自动调用,并且在对象的生命周期内只调用一次
4.构造方法可以重载
5.如果用户没有显式定义,编译器会自动生成一份默认的构造方法,生成的默认构造方法一定是无参的。一旦用户定义,编译器就不再生成。
6.构造方法中,可以通过this调用其他构造方法来简化代码,注意
(1).this()只能在构造方法里面调用构造方法,不能把this()放在一个普通的方法里面调用构造方法!
(2).构造方法当中,不能调用自己
(3).只能放在构造方法内部的第一行
(4).不能形成环,比如在第一个构造方法里面调用第二个,再在第二个构造方法里面调用第一个。
在上文中提出的第二个问题:为什么局部变量在使用时必须要初始化,而成员变量可以不用呢?
先看个例子
class Student
{
public int age;
public String name;
Student()
{
System.out.println("没有参数的构造方法!");
}
Student(int age,String name)
{
this.age=age;
this.name=name;
}
public void print()
{
//this(18,"xiaobai");
System.out.println("年龄:"+this.age+" "+"姓名:"+this.name);
}
}
public class Class_And_Object2
{
public static void main(String[] args)
{
Student student1=new Student();
student1.print();
}
}
这个代码运行的结果就是下面这个
原因是:成员变量在没有初始化的时候,都有一个默认值
最后一行的意思就是引用类型的默认值是null,比如String
下面这个图的意思就是student2不指向任何对象
**面向对象程序三大特性:封装、继承、多态。**而类和对象阶段,主要研究的就是封装特性。何为封装呢?简单来说就是套壳屏蔽细节。
比如:对于电脑这样一个复杂的设备,提供给用户的就只是:开关机、通过键盘输入,显示器,USB插孔等,让用户来和计算机进行交互,完成日常事务。但实际上:电脑真正工作的却是CPU、显卡、内存等一些硬件元件。对于计算机使用者而言,不用关心内部核心部件,比如主板上线路是如何布局的,CPU内部是如何设计的等,用户只需要知道,怎么开机、怎么通过键盘和鼠标与计算机进行交互即可。因此计算机厂商在出厂时,在外部套上壳子,将内部实现细节隐藏起来,仅仅对外提供开关机、鼠标以及键盘插孔等,让用户可以与计算机进行交互即可。
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行 交互
看下面这个代码,原本String前面的权限是public(公开的),现在我改成了private(私有的),
第二个红色圈圈的name就变红了,也就是报错了,原因是我的权限改成了私有的之后,在类的外面就看不到这个name这个变量了,故也访问不到了
private就是
default的意思就是什么都不加
比如
这两个成员变量前面既没有public,又没有private
public:可以理解为一个人的外貌特征,谁都可以看得到
private:只有自己知道,其他人都不知道
protected主要是用在继承中,继承部分详细介绍
default权限指:什么都不写时的默认权限
访问权限除了可以限定类中成员的可见性,也可以控制类的可见性
如上图,我们的age和name被private了,有人会问,你封装了之后,它们只能在类内使用呀,类外不能用啊,这该怎么办呢?
仔细想想,我们把细节隐藏起来了之后,我们要向外面提供公开的接口,这些公开的接口怎么实现呢?很简单,写几个方法就可以了!
class Student
{
private int age;
private String name;
public void setName(String name)
{
this.name=name;
}
public String getName()
{
return this.name;
}
public void setAge(int age)
{
this.age=age;
}
public int getAge()
{
return this.age;
}
Student()
{
//System.out.println("没有参数的构造方法!");
}
Student(int age,String name)
{
this.age=age;
this.name=name;
}
public void print()
{
System.out.println("年龄:"+this.age+" "+"姓名:"+this.name);
}
}
public class Class_And_Object2
{
public static void main(String[] args)
{
Student student=new Student();
student.setAge(10);
student.setName("小白");
student.print();
}
}
有人问了,这只有两个属性,设置getXXX和setXXX还比较方便,如果很多属性呢?
在IDEA里,我们可以这样
按住Shift将属性全选
就可以自动生成出来了
在面向对象体系中,提出了一个软件包的概念,即:为了更好的管理类,把多个类收集在一起成为一组,称为软件包。有点类似于目录。比如:为了更好的管理电脑中的歌曲,一种好的方式就是将相同属性的歌曲放在相同文件下。
在Java中也引入了包,包是对类、接口等的封装机制的体现,是一种对类或者接口等的很好的组织方式,比如:一个包中的类不想被其他包中的类使用。包还有一个重要的作用:在同一个工程中允许存在相同名称的类,只要处在不同的包中即可。
Java 中已经提供了很多现成的类供我们使用. 例如Date类:可以使用 java.util.Date 导入 java.util 这个包中的 Date类.
当两个包底下都存在Date这个类的时候,编译器懵了
将第一个图和第二个图右边的代码块结合起来,最好不要用第二个图左边的代码的带*的形式
这个时候就要指定用哪个包的Date了
1.在文件的最上方加上一个 package 语句指定该代码在哪个包中.
2.包名需要尽量指定成唯一的名字, 通常会用公司的域名的颠倒形式(例如 com.xiaobai.www )
3.包名要和代码路径相匹配. 例如创建 com.xiaobai.www 的包, 那么会存在一个对应的路径 com/xiaobai/www 来存储代码.
4.如果一个类没有 package 语句, 则该类被放到一个默认包中.
在IDEA中
5.包名要小写
一个叫com.xiaobai的包就建好了
再在包中创建类
可以看到,在com.xiaobai这个包下面创建了一个叫aaa的包和一个Testbai的类
通过上述代码将aaa包中的Testaaa类导入到了Testbai这个类里面了
import就是导入其他包当中的类
现在我们来说default,也就是什么限定符都不加
default它只能在同一个包里面访问
看下面两个图,我在Testaaa这个类里面定义了一个名字,然后将Testaaa这个类导入到Testaaa2这个类里面,然后在Testaaa2里面创建Testaaa的对象,然后输出testaaa.name这个名字,就是错的,因为Testaaa和Testaaa2分别在包aaa和包aaa2下面,属于两个包,如果在String name前面加一个public就不会错了
如果类全部在一个包底下
那么String前面啥也不加就不会错了
我们假设很多学生在一个教室上课,
所以我们要在每个对象的后面都加上这个教室的教室号
这三个333都占据了空间,但是因为他们都是一个班的学生,我们只想要一个333,故就有了static这个关键字
原本你的三个classroom都在堆区,这个时候只有一个classroom了,它就放到你的方法区了,这个时候我们就把classRoom叫做类变量(静态成员变量),不加static修饰的成员变量叫做普通成员变量
普通成员变量:每个对象都有一份
静态成员变量:所有对象共用一份
现在为止我们一共学习过以下三种变量
在Java中,被static修饰的成员,称之为静态成员,也可以称为类成员,其不属于某个具体的对
象,是所有对象所共享的。
static修饰的成员变量,称为静态成员变量,静态成员变量最大的特性:不属于某个具体的对象,是所有对象所共
享的。
【静态成员变量特性】
class Student
{
private int age;
private String name;
public static String classRoom="333";//教室就只有一份了
public Student(int age, String name)
{
this.age = age;
this.name = name;
}
public void print()
{
System.out.println("年龄:"+this.age+" "+"姓名:"+this.name);
}
}
public class Class_And_Object2
{
public static void main(String[] args)
{
Student student1=new Student(18,"小白");
Student student2=new Student(19,"小红");
Student student3=new Student(17,"小蓝");
System.out.println("教室是:"+ Student.classRoom);//这两行都可以,推荐这一行,因为这个时候classroom这个成员变量已经不属于对象了,所以不推荐下面一行的形式
System.out.println("教室是:"+ student1.classRoom);
}
}
下面来看个题
答案是102
引用可以指向引用吗?
不可以!
下图student1其实只保存了最新的“zhangsan3”这个对象
下面这段代码的结果是333,因为classRoom已经不属于对象了
class Student
{
private int age;
private String name;
public static String classRoom="333";//教室就只有一份了
public Student(int age, String name)
{
this.age = age;
this.name = name;
}
public void print()
{
System.out.println("年龄:"+this.age+" "+"姓名:"+this.name);
}
}
public class Class_And_Object2
{
public static void main(String[] args)
{
Student student1=null;
System.out.println("教室是:"+ student1.classRoom);
}
}
静态方法不依赖对象,所以不能在静态方法里面调用依赖对象的方法
因为print是依赖对象的,通过对象.引用来调用
而静态方法是通过类名.引用来调用的
这就是为什么在main方法里面调用别的方法的时候,别的方法要加上static
Java中,被static修饰的成员方法称为静态成员方法,是类的方法,不是某个对象所特有的。静态成员一般是通过静态方法来访问的。
【静态方法特性】
注意:静态成员变量一般不会放在构造方法中来初始化,构造方法中初始化的是与对象相关的实例属性
静态成员变量的初始化分为两种:就地初始化 和 静态代码块初始化。
构造块:定义在类中的代码块(不加修饰符)。也叫:实例代码块。构造代码块一般用于初始化实例成员变量
class Student {
private int age;
private String name;
public Student(int age, String name)
{
this.age = age;
this.name = name;
System.out.println("带两个参数的构造方法");
}
//这个就是构造块,用来初始化实例成员变量的
{
this.name="小白";
System.out.println("构造块");
}
public int getAge()
{
return age;
}
public void setAge(int age)
{
this.age = age;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
}
public class Class_And_Object4 {
public static void main(String[] args)
{
Student student=new Student(20,"大白");
}
}
运行结果如下
可以看到,构造块执行顺序在构造方法之前
这个时候,name被赋值成大白,因为构造方法比构造块运行顺序靠后
使用static定义的代码块称为静态代码块。一般用于初始化静态成员变量。
可以用静态代码块来给静态成员变量赋值
private static String classroom;
static
{
classroom="888";
}
不能在静态代码块里面加上this.name类似的依赖对象的内容,因为静态的不依赖对象
class Student {
private int age;
private String name;
private static String classroom;
static
{
classroom="888";
System.out.println("静态代码块");
}
public Student(int age, String name)
{
this.age = age;
this.name = name;
System.out.println("带两个参数的构造方法");
}
//这个就是构造块,用来初始化实例成员变量的
{
this.name="xiaobai";
System.out.println("构造块");
}
public int getAge()
{
return age;
}
public void setAge(int age)
{
this.age = age;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
}
public class Class_And_Object4 {
public static void main(String[] args)
{
Student student=new Student(20,"xiaobai");
}
}
这个意味着构造代码块和构造方法只会在实例化(创建)对象的时候才会被调用,而静态代码块意味着只要你的类被加载的时候就会被调用
class Student {
private int age;
private String name;
public static String classroom;
static
{
classroom="888";
System.out.println("静态代码块");
}
public Student(int age, String name)
{
this.age = age;
this.name = name;
System.out.println("带两个参数的构造方法");
}
//这个就是构造块,用来初始化实例成员变量的
{
this.name="xiaobai";
System.out.println("构造块");
}
}
public class Class_And_Object4 {
public static void main(String[] args)
{
// Student student=new Student(20,"xiaobai");
System.out.println(Student.classroom);
}
}
如果这样,静态代码块在静态成员变量前,这时候classroom被赋值203,这个时候是按住摆放顺序执行的
构造代码块也一样,这时候name就是88888,按照先后顺序
从书写习惯来说,先写成员变量,再写其他的
1.静态代码块不管生成多少个对象,其只会执行一次
class Student {
private int age;
private String name;
public static String classroom;
static
{
classroom="888";
System.out.println("静态代码块");
}
public Student(int age, String name)
{
this.age = age;
this.name = name;
System.out.println("带两个参数的构造方法");
}
//这个就是构造块,用来初始化实例成员变量的
{
this.name="xiaobai";
System.out.println("构造块");
}
}
public class Class_And_Object4 {
public static void main(String[] args)
{
Student student1=new Student(20,"xiaobai");
System.out.println("---------------------------------------");
Student student2=new Student(21,"xiaobaibai");
}
}
可以看到,即使再创建一个对象,但是第二个对象不会再调用静态代码块了
因为类只会被加载一次,所以静态的只会被执行一次
2.静态成员变量是类的属性,因此是在JVM加载类时开辟空间并初始化的
3.如果一个类中包含多个静态代码块,在编译代码时,编译器会按照定义的先后次序依次执行(合并)
//在编译器看来只有一个static,并且里面的内容会被合并
static
{
classroom="888";
System.out.println("静态代码块1");
}
static
{
System.out.println("静态代码块2");
}
static
{
System.out.println("静态代码块3");
}
4.实例代码块只有在创建对象时才会执行
(上面已经举例了)
代码块一般不怎么用,考试的时候可能会用到
当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么这个内部的完整结构最好使用内部类。在 Java 中,可以将一个类定义在另一个类或者一个方法的内部,前者称为内部类,后者称为外部类。内部类也是封装的一种体现。
public class OutClass
{
class InnerClass
{
}
}
// OutClass是外部类
// InnerClass是内部类
即未被static修饰的成员内部类。
public class OutClass
{
public int data1=10;
private int data2=20;
private static int data3=30;
class InClass
{
private int a;
public int b;
//private static int c;//实例内部类里面不能定义静态成员变量
public void print()
{
System.out.println("实例内部类");
}
}
}
public class OutClass
{
public int data1=10;
private int data2=20;
private static int data3=30;
class InClass
{
private int data4=40;
public int data5=50;
//private static int c;//实例内部类里面不能定义静态成员变量
public void print()
{
System.out.println(data1);
System.out.println(data2);
System.out.println(data3);
System.out.println(data4);
System.out.println(data5);
System.out.println("实例内部类");
}
}
}
在内部类中不能定义静态的成员变量,因为被static修饰的变量不依赖对象,但是内部类依赖对象,怎么办呢?加一个final,因为final修饰的是常量,不是变量,常量是在编译的时候就已经确定了
如果在内部类再顶一个data1,print函数的运行结果的data1就是内部类的data1,不是外部类的data1,怎么访问外部类的data1呢
第一行就是外部类的data1的值,第二行就是内部类的data1的值
由此我们得出一个结论:实例内部类是存在外部类的this的
内部类和外部类同用一个java源文件,但是经过编译之后,内部类会形成单独的字节码文件
【注意事项】
一般实例内部类不好,我们要定一个实例内部类对象的前提,还要定义一个外部类对象,故我们要定义一个实例内部类的对象,就要定义两个对象,那么我们可不可以只定义一个对象呢?当然可以,下面就是我们的静态内部类
OuterClass2.InnerClass2()是一个整体
2.如何在内部类当中访问外部类的非静态类成员
定义一个外部类对象
public class OutClass
{
public int data1=10;
public int data2=20;
public int data3=30;
static class InnerClass
{
public int data4=40;
public int data5=50;
public void print()
{
OutClass outClass=new OutClass();
System.out.println(outClass.data1);
System.out.println(outClass.data2);
System.out.println(outClass.data3);
System.out.println(data4);
System.out.println(data5);
}
}
public static void main(String[] args)
{
OutClass.InnerClass innerClass=new OutClass.InnerClass();
innerClass.print();
}
}
定义在方法里边的类,该种内部类只能在其定义的位置使用,一般使用的非常少,此处简单了解下语法格式。
只能在对应的方法里面实例化对象
public class OutClass
{
public void method()
{
class InnerClass
{
public int data=10;
}
InnerClass innerClass=new InnerClass();
}
}
【注意事项】
interface Shape
{
void draw();
}
public class OutClass
{
public static void main(String[] args)
{
//类名叫啥我不太清楚--匿名内部类
new Shape(){
public void draw()
{
}
};
}
}
当我们实例化一个对象的时候,打印对象名称打印出来的是地址
这个地址是通过println来打印出来的
println的底层逻辑是这样的
我们可以看到,最终的println是通过tostring这个方法实现的,那么我们可不可以自己写一个toString方法呢?
当然可以
我们让我们自己写的toString这个方法的名字和最终println调用的toString这个方法的名字一模一样
class Student
{
public String toString()
{
return "小白";
}
}
public class Class_And_Object4 {
public static void main(String[] args)
{
Student student=new Student();
System.out.println(student);
}
}