类和对象
在认识类和对象之前,我们先来了解一下OOP语言,所谓OOP语言就是面向对象的语言,那么我们先来简单了解OOP语言有三大特征:
oop有三大特征:封装,继承,多态。
封装:封装就是通过访问权限修饰符,将类的属性和方法进行私有化,不让外部直接访问,而是通过专门的接口进行访问。
继承:当一个类继承了另一个类,那么这个类就是另一个类的子类,另一个类是这个类的父类。子类会继承父类全部的属性和方法,并且可以在这个基础上扩展自己的属性和方法
继承极大的提高了代码的耦合性,子类可以继承父类的方法,一定程度上简化了代码,但是也很大程度上限制了代码的灵活性,耦合性过高会使得后续代码的改进变得极为繁杂,容易牵一而动全身。
多态:指一个类相同的方法在不同情况下有不同的表现
转自:oop三大特征_驼爷的博客-CSDN博客
所谓类和对象是抽象的概念:
类其实就是自定义类型或者说是模板,而对象就是这些模板产生的一个个实体。比如我们在建房的时候,一个模板可以建造出很多所房子,里面的户型都是一样的。
我们通过一个类就可以产生很多个对象,
C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。
JAVA是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成。
面向过程注重的是过程,在整个过程中所涉及的行为,就是功能。
面向对象注重的是对象,也就是参与过程所涉及到的主体。是通过逻辑将一个个功能实现连接起来
例如:
面向过程: 1.把冰箱打开 2. 把大象放入 3. 冰箱关起来
面向对象: 打开冰箱,储存,关闭都是对冰箱的操作,是冰箱的行为。冰箱就是一个对象,所以只要操作冰箱所具备的功能,都要定义在冰箱中。
【面向对象概念】
1.面向对象是思考问题的一种思考方式,是一种思想。比如:概念与实例。理论与实践。名和实等等。。
比特科技
2.类就是一类对象的统称。对象就是这一类具体化的一个实例。
3.面向对象的好处:将复杂的事情变简单了,只要面对一个对象就行。
【面向对象设计】
面向对象设计把握一个重要的经验:谁拥有数据,谁对外提供操作这些数据(私有)的方法!
(被动的一方是数据的拥有者,主动的一方是执行者)
开发时:找对象,建对象,用对象,并维护对象之间的关系。
后期学习过程当中,我们会就这三点进行深入学习。
简而言之
面向对象就是用代码(类)来描述客观世界的事物的一种方式. 一个类主要包含一个事物的属性和行为
定义类,产生一个对象
//使用class前缀就可以定义一个类
class Person{
}
public static void main (String[] args)
{
//然后我们用我们定义的类的名字作为类型名,就可以创建一个这个类的对象
Person person;
}
一个类是由字段和方法组成的,当然这两个不是必须要有的,但是如果没有这里两个东西,这个类就是不使用的。
class Person{
field//字段
method//方法
}
现在我们来写出简单字段你和方法:
class Person{
//字段 -> 成员变量
String name;
int age;
public static int siza = 10;
//方法 ->
public void eat(){
int a = 10;
System.out.println("eat");
}
public void sleep(){
System.out.println("sleep");
}
}
上面的name和age就是全局变量,他是定义在方法外面类的里面,而定义在方法里的变量就是局部变量,如上述的 a。
而上述的实例成员对象储存在对象里面,对象在堆上:
实例成员没有初始化,默认值为对应的零值:引用类型默认为null,简单类型默认为0。
特殊:char : '\u0000' boolean : fasle
访问修饰限定符:
public:公有的
private:私有的
protect:受保护的
什么都不写:默认权限,也叫做包访问权限。
访问类中的实例化对象中的实例成员变量
使用操作符: .
格式:
对应的引用.成员变量
//实例化一个对象,通过关键字new
Person person1 = new Person();
System.out.println(person1.name);//null
System.out.println(person1.age);//0
对实例化成员对象进行修改:
person1.name = "zhangsan";
System.out.println(person1.name);//zhangsan
静态成员变量:(其中的size)
class Person{
//字段 -> 成员变量
String name;
int age;
//静态成员变量
public static int size;//它也可以不初始化,不初始化的结果为0值。
//方法 ->
public void eat(){
System.out.println("eat");
}
public void sleep(){
System.out.println("sleep");
}
}
我们也可以通过操作符:. 来访问静态成员变量:
public class textdome{
public static void main (String[] args)
{
System.out.println(person1.size);//0
}
静态成员变量也可以不初始化,不初始化值和实例成员变量是一样的。
静态成员变量不储存在对象中,而是储存在方法区中,和类储存在一起,
访问一个静态成员变量,可以通过其实例化的对象来进行访问:
class Person{
//字段 -> 成员变量
String name;
int age;
//静态成员变量
public static int size;//它也可以不初始化,不初始化的结果为0值。
//方法 ->
public void eat(){
System.out.println("eat");
}
public void sleep(){
System.out.println("sleep");
}
public static void haha()
{
System.out.println("haha");
}
}
public class textdome{
public static void main (String[] args)
{
//实例化一个对象,通过关键字new
Person person1 = new Person();
System.out.println(person1.size);
}
但是会报警告:
我们还可以用类名来访问:
此时不会报警告。
实例成员方法
实例成员方法就是不用static修饰的方法:
我们访问实例成员方法就需要实例化的对象来访问:
Person person1 = new Person();
person1.eat();
person1.sleep();
静态方法
静态方法和静态成员变量是一样的,我们可以用一个实例化的对象去访问,当然也会报警告:
我们一般使用这个所属类名去访问:
而且在静态方法内部是不能访问非静态数据成员的:
我们发现此时报错了。
在静态方法里调用非静态的方法也是不行的:
此时也报错了。
也就是说,静态的只能调用静态的。
多个对象
如果我们在main函数中创建多个对象,那么每一个对象都会在堆上创建一个存储这个对象信息的一块空间:
静态成员变量在方法区中存储,只存储一个。
也就是说,普通的全员变量,在这个类的不同对象中修改,那么修改的只是这个对象中的全员变量 的。:
public static void main(String[] args) {
Person person1 = new Person();
Person person2 = new Person();
person1.age++;//1
person1.age++;//2
person2.age++;//1
System.out.println(person1.age);//2
System.out.println(person2.age);//1
}
但是静态的就不一样了,因为静态全局变量储存在方法区中,和类存储在一起,我们在不同对象中修改一个静态全局变量的值,修改的是一个变量的值,操作是会存储的:
public static void main(String[] args) {
Person person1 = new Person();
Person person2 = new Person();
person1.size++;//1
person2.size++;//2
System.out.println(Person.size);//2
}
封装
封装的本质就是让类的调用者不必太多的了解类的实现者是如何实现类的, 只要知道如何使用类就行了。
用private来修饰属性或方法;我们之前都是用public来写的,但是用public来写有一个问题:就是我们在类中定义的全局变量,在类外也可以进行使用和修改。
而所谓封装就是使用private来修饰属性和方法,使得类当中的全局变量和方法只能再类当中使用。
那么为什么要对垒进行封装呢?
比如一下代码:
class Person{
//字段 -> 成员变量
String name;
int age;
public static void func1()
{
eat();
}
//静态成员变量
public static int size;//它也可以不初始化,不初始化的结果为0值。
//方法 ->
public void eat(){
System.out.println("eat");
}
public void sleep(){
System.out.println("sleep");
}
public static void haha()
{
System.out.println("haha");
}
}
这段代码是类的实现者所写的代码
public static void main(String[] args) {
Person person1 = new Person();
Person person2 = new Person();
person1.size++;//1
person2.size++;//2
System.out.println(Person.size);//2
}
这个是类的调用者调用类用的代码。
那么以后再公司里写代码的时候,不可能每一个类都是你写的代码,所以别人在实现它的类的时候,我们不知道他的类里面有什么属性。假设我们在调用的时候,像上述代码一样调用了name,那么实现者有一天把name的名字改为Myname了,那么调用者是不知道的。
所以我们要对这个name进行封装,是这个name只能再这个类中使用。那么我们再用private修饰name之后,调用者如何使用这个name呢?
我们可以实现一个接口:
//输入接口
public String setname(String name1)
{
this.name = name1;
}
//输出接口
public String getName()
{
return this.name;
}
注意:要记住写this来访问同类中的变量。
为什么要在此处调用this来访问呢?此处我们不使用this也是可以运行的
之所以使用this是因为:如果我们的成员变量和局部变量取的名字都一样,假设都是name,那么我们在使用或修改name 的时候,他会优先使用或修改局部变量的name,而不是全员变量。那么我们的函数就不能达到效果。所以我们用this来表示此处的name使用或修改的是本类中的name。
然后我们就可以在类外这样使用这两个接口了:
public static void main(String[] args) {
Person person1 = new Person();
person1.setname("zhangsan");
String str = person1.getName();
System.out.println(str);//zhangsan
}
由此我们就实现对类内的name这个全员变量的使用和修改。
在公司里面,某一个属性或方法一旦被修改成public,没有人会轻易的去修改它。
idea里的快捷键,快速生成类中变量的输出函数:
Alt + ins键:
点击toString()。
选择你要生成方法的变量点击ok
就可以生成方法;
调用:
System.out.println(person1.toString());//Person{name='zhangsan', age=0}
当我们没有重写toString()方法的时候会打印下面的结果:
构造方法
如图,我们创建对象的时候,如上的 new Person();做了两步工作:
- 给对象分配内存
- 调用合适的构造方法
而构造方法就是 他的方法名是和类名相同的,且没有返回值。
当然,方法前的修饰词public private的这些都可以。
比如我们定义一个类,在这个类中定义一个构造方法:
class Animal{
public Animal(){
System.out.println("这是animal 类的构造方法");
}
}
然后再创建这个类的一个对象:
public static void main(String[] args) {
Animal animal = new Animal();//这是animal 类的构造方法
}
我们发现此时没有调用方法,也没有使用类似println()的方法,但是在屏幕上打印如下结果:
也就是说,我们在new Animal()的时候调用了这个Animal这个方法,但是构造函数不创建也是不会报错,不会报警告,任然可以正常使用这个类。当我们没有在类中定义构造方法,那么编译器会默认帮我们定义一个不在参数的构造方法。其实我们在new的时候就是在调用这个类的构造方法。
那么当我们在类中定义了一个构造函数,那么编译器就不会再帮我们定义构造函数了。也就是说,此处需要注意的是,假设我们只创建了一个带有参数的构造方法的时候,我们之前所使用的不在参数的构造方法就不能用了:
class Animal{
int age;
String name;
public Animal(int age,String name){
System.out.println("这是animal 类的构造方法");
}
}
public static void main(String[] args) {
Animal animal = new Animal();//这是animal 类的构造方法
}
我们发现此时创建对象这里面,我们所使用的Animal()这里报错了:
构造方法可以不止一个:
class Animal{
int age;
String name;
public Animal(){
System.out.println("这是animal 类的构造方法");
}
public Animal(int age){
return this.age;
}
public Animal(int age,String name){
this.name = name;
this.age = age;
}
}
也就是说,我们可以在创建对象的时候,把需要的值传进来,不需要set和get方法。但是set和get方法在操作类里面的全员变量的时候更加方便。一般都是使用set和get方法来操作类中的数据。
也就是说构造方法是支持方法的重载的。
问:this关键字代表当前对象,这句话对不对?
不对,this是代表的是当前对象的引用,看下面这代码:
我们刚刚说过,构造对象包括两步,第二步是要调用合适的构造方法,那么上述例子,我们在Person方法调用完之前,就已经使用了this来代表这个当前对象,那么就和题目矛盾了。因为此时对象还没有完全创建完。
this功能
class Person{
//字段 -> 成员变量
String name;
int age;
public void setname(String name1)
{
this.name = name1;
}
public String getName()
{
return this.name;
}
}
this.dara() : 访问当前对象的方法:(如下eat()方法中的例子)
class Person{
//字段 -> 成员变量
String name;
int age;
public String getName()
{
return this.name;
}
public void eat(){
System.out.println("eat");
this.getName();
}
}
this() : 访问当前对象的构造方法:
class Animal{
int age;
String name;
public Animal(){
this(18,"zhangsan");
System.out.println("这是animal 类的构造方法");
}
public Animal(int age){
System.out.println("这是animal 类的构造方法");
}
public Animal(int age,String name){
System.out.println("这是animal 类的构造方法");
}
}
此时我们调用Animal() 方法就会调用其他两个构造方法。
需要注意的是,这 this() 只能写在构造方法中,其他方法是不能写的。
还需要注意的是,一个构造方法中只能用一个 this() ,而且必须放在第一行:
我们在写代码的时候,比如在类中,要习惯用this。
作用
构造方法的作用就是用来实例化对象的,或者说是用来创建对象的。
代码块
本地代码块:基本不用
在方法中创建一个代码块:
public void eat(){
//本地代码块
{
}
System.out.println("eat");
this.getName();
}
实例代码块/构造代码块:
class Animal{
int age;
String name;
public Animal(){
System.out.println("这是animal 类的构造方法");
}
public Animal(int age){
System.out.println("这是animal 类的构造方法");
}
public Animal(int age,String name){
System.out.println("这是animal 类的构造方法");
}
//实例代码块
{
System.out.println("实例代码块·······");
}
}
这个就是一个实例代码块,我们发现这个就是用中括号括起来的代码,我们在这个代码块中可以使用修改类中的变量,也可以调用其中的方法。但是这种方式的代码一般在现实生活中不会用到。
那么实例代码块有什么特性呢?
静态代码块:
static {
System.out.println("静态代码块");
}
如上述代码,相比于实例代码块,静态代码块用static修饰。
同样的,因为是静态的,所以还不能访问全员变量。
静态方法和静态的成员是不依赖的对象的,我们可以用类名类进行访问,如果静态的方法里面,访问了实例成员变量,那么这个实例成员变量是依赖对象的,但是静态方法是不依赖对象的,这就产生了矛盾。
静态代码块和实例代码块的区别
class Animal{
int age;
String name;
public Animal(){
System.out.println("这是animal 类的构造方法");
}
public Animal(int age){
System.out.println("这是animal 类的构造方法");
}
public Animal(int age,String name){
System.out.println("这是animal 类的构造方法");
}
//实例代码块
{
System.out.println("实例代码块·······");
}
//静态代码块
static {
System.out.println("静态代码块");
}
}
比如上面这个类,有实例代码块,静态代码块和构造方法,那么我们接下来创建一个这个类的对象:
public static void main(String[] args) {
Animal animal = new Animal();//这是animal 类的构造方法
}
发现打印这么一个结果:
我们发现,我们没有调用任何方法,但是,执行了实例代码块和静态代码块中的打印代码。
而且我们上述在实现类的时候,是先写的实例代码块,在写的静态代码块,但是打印结果却是反的,而且构造函数打印结果是在最后的。
然后我们把静态和实例代码块的书写位置进行交换,发现还是这个打印结果。也就说在初始化的时候,先初始化静态代码块,在初始化实例代码块内容,最后初始化的是构造方法中的内容。
来看这个代码:
public static void main(String[] args) {
Animal animal1 = new Animal();//这是animal 类的构造方法
System.out.println("==========这是一个分界线");
Animal animal2 = new Animal();
}
我们同时实例化两个对象,打印结果却不是之前结果的两次打印:
我们发现,在第二次实例化对象的时候,并没有初始化静态代码块中的内容。
结论:只要是静态的,在执行的时候,就只执行一次,而且是最先被执行的。
那么我们就可以理解,为什么之前的静态方法中不能使用和修改类中的全员变量的了:
因为静态的是最先被初始化的,在静态方法被初始化的时候,全员变量还没有被初始化,此处就产生了矛盾,所以在编译器就直接不给我们在静态方法中调用全员变量。
如果有两个静态的方法或变量,在初始化和执行的时候,就按照定义的前后顺序来进行,同理同为实例代码块也是如此。
匿名对象
没有引用的对象称为匿名对象。
顾名思义就是没有名字的对象,我们之前的对象都是会创建名字的,然后再用这对象来使用类中的方法和属性,如下:
Animal animal1 = new Animal();//这是animal 类的构造方法
animal1.sleep();
我们也可以不用名字直接创建一个对象来使用类中的方法和属性:
new Animal().sleep();
上述操作就是匿名对象的使用。
假设我们需要多次调用一个类中的一个方法或者是属性,如果我们使用的有名对象来进行访问,那么我们只需要创建一个对象就可以连续访问了,但是匿名对象虽然也能进行连续访问,但是每一次访问的时候都需要创建一个对象来进行访问,这就浪费了内存储存空间:
animal1.sleep();
animal1.sleep();
new Animal().sleep();
new Animal().sleep();
new Animal().sleep();
也就是说,每一次调用类中的方法和属性的时候,使用匿名对象访问,每一次都需要创建一个对象。
而且匿名对象只能再创建对象的时候使用。
我们一般在某一方法或属性只是用一次,以后就不用了,可以考虑用匿名对象。