java从入门到精通四(面向对象上,类与对象的实现,代码块的执行顺序)

简介: 一 :面向对象的概念面向对象的方法主要是把事物对象化,包括属性和行为。而面向对象编程则比较贴近生活的实际,也就说,在程序中的编程,力图对描述的事物在现实中的形态保持一致。为了做到这一点,我们java中也就有类和对象的概念。类是对一类事物的抽象,类下就有若干对象,对象用于描述类中事物的个体。说来光看定义还是比较抽象,不容易理解。我们在下面的编程实践中可以大致理解这种思维模式。

一 :面向对象的概念


面向对象的方法主要是把事物对象化,包括属性和行为。而面向对象编程则比较贴近生活的实际,也就说,在程序中的编程,力图对描述的事物在现实中的形态保持一致。为了做到这一点,我们java中也就有类和对象的概念。类是对一类事物的抽象,类下就有若干对象,对象用于描述类中事物的个体。说来光看定义还是比较抽象,不容易理解。我们在下面的编程实践中可以大致理解这种思维模式。


二、类与对象



我们认为类是一类事物的抽象,我们参考上图,图纸代表了类,它其实就是一个抽象的概念,图纸有设计汽车的方法,包括汽车的功能等等,但终究只是构思,没有实物,而当它设计出来实物汽车,汽车就被称之为对象。对象是实例化的。我们大概这样去理解。

以下两图摘自菜鸟教程,比较形象。




那么,具体的编程中我们如何实现这种思想呢?请继续浏览。


三、类与对象的编程实现


1.设计类

<1>java中类的种类

这里浅谈一下java类的种类

我们取典型的


内部类

1:成员内部类

所谓成员内部类,那么就说明了该类的级别等同于成员变量和方法,属于同一级别。

我们来看


package java_practice;
public class Student {
 private String name;
 private int age;
 public String place;
 static int number = 666;
 String name_1 = "jgdabc";
 static class Person{
     public  String person = "李明";
     void score()
     {
         System.out.println("我是静态内部类方法");
     }
    void sc()
    {
        System.out.println("我是静态内部类,我可以直接访问到外部类成员number:"+number);
        System.out.println();
    }
 }
 public static void main(String args[])
 {
     Person p_p = new  Person();
     p_p.score();
     p_p.sc();
 }
 public Student(int age)
 {   this.age = age;
     System.out.println("我今年"+age+"岁");
 }
 public Student() {
 }
 // public Student(){
 //     System.out.println("Hello");
 // }
 public void describe()
 {
     System.out.println("学生:"+name_1);
 }
 public int getAge() {
     return age;
 }
 public void setAge(int age) {
     if(age<0||age>200)
     {
         System.out.println("赋值不合理");
     }
     System.exit(0);//甚至你可以直接退出
     this.age = age;
 }
 public String getName() {
     return name;
 }
 public void setName(String name) {
     this.name = name;
 }
}


Student 类中定义了一个内部类Person,并定了一个方法,静态内部类是可以直接访问到外部类的,但是外部类不能直接访问到内部类成员的。定义为静态内部类的原因是为了让在main方法对其操作。如果不加static,那么无论如何都访问不了,new对象也是不可以的。



2:方法内部类


顾名思义,该类是定义在方法中的,只能被本方法所使用,所以就不可以给public,static权限了。和局部变量一下,方法外部无法访问。

如下sc1便是方法内部类



抽象类


abstract修饰的类


抽象类是不能创建对象的,也就是不能实例化。在抽象类中声明的抽象方法没有方法体。抽象类可有自己的构造方法。可以让子类进行初始化。抽象类一般都是需要子类取具体的实现的。


abstract class Emoplyee {
    String name;
    public Emoplyee(String name) {
    this.name = name;
    }
    public void test() {
    }
    public abstract void work();
    }
  class Waiter extends Emoplyee {
    public Waiter(String s) {
    super(s);
    }
    public void work() {
    System.out.println("doing");
    }
  }


接口类


interface修饰的类


接口中可以有多个方法,但是只能是抽象方法,和抽象类不同,抽象类中可以有非抽象方法。接口中是不可以有具体的方法和成员变量的。没有构造方法,而且不能示例话,只能被实现。接口之间也具有继承关系,并且,接口只能继承接口。一个类可以实现多个接口,且若实现,必须实现接口中的方法。


package java_practice;
public interface Person {
    int age = 18;
    void describe();//没有方法体
}
package java_practice;
public class Person_rea implements Person {
    @Override
    public void describe() {
        System.out.println("Are you sb?");
        // TODO Auto-generated method stub
    }
    public static void main(String args[])
    {
        Person_rea  p = new Person_rea();
        p.describe();
    }
}



object类


很简单,类祖宗。


如果我们要描述一个学校中所有学生的信息,我们就需要设计一个学生类。类是对同类事物的抽象,这里的一个学校的所有学生就是视为同类的,都是学生。


创建类的格式


[修饰符] class 类名称
  {
      //类的成员变量
      //类的方法
  }


其中,修饰是用来修饰类的。常用的修饰符有public,private,protected.当然你根据需要也可以省略休书符。如果省略的话,默认就是public。有关这些修饰符的一些作用。会涉及一些访问权限,这些会在面向对象的学习中慢慢渗透。



创建一个学生类:


public class Student {
  String name;//定义一个姓名属性
  int age;//年龄属性
  public void introduce(){
  System.out.println("大家好,我叫"+name+"我今年"+age+"岁");
  }
}


这里得Student为一个类名。


类中我们可以定义属性,如上面学生类中的属性,name,age。我们联系实际,学生本身就有姓名和年龄的属性。我们在这里编程,把它们定义出来了。所以面向对象是比较和实际相联系的。


一个类可以包含以下类型变量:


局部变量:在方法、构造方法或者语句块中定义的变量被称为局部变量。变量声明和初始化都是在方法中,方法结束后,变量就会自动销毁。


成员变量:成员变量是定义在类中,方法体之外的变量。这种变量在创建对象的时候实例化。成员变量可以被类中方法、构造方法和特定类的语句块访问。


类变量:类变量也声明在类中,方法体之外,但必须声明为 static 类型。


2:对象的分析


一个类要想真正的进行操作,那么必须依靠对象来实现。

对象的定义格式如下:

类名称 对象名称 = new 类名称() ;


如果要想访问类中的属性或方法(方法的定义),则可以依靠以下的语法形式:

访问类中的属性:对象.属性 ;

调用类中的方法:对象.方法();


在java中对象的声明有两层含义:

声明:Student student = null;//表示声明了一个对象,但是对象无法使用,student没有具体的内存指向。


实例化对象:Student student = new Student();//表示实例化对象,可以具体去使用。

引入:调用方法:

student.introduce();//通过对象调用

new Student().introduce();//匿名对象调用;

了解对象实现过程与具体内存的关系,可以帮助理解



对象是存放在堆中,还是栈中呢?



创建一个对象的时候,到底是在栈中分配还是在堆中分配需要看2个方面:对象类型和在Java中存在的位置


1.如果是基本数据类型,byte、short、int、long、float、double、char,如果是在方法中声明,则存储在栈中,其它情况都是在堆中(比方说类的成员变量就在堆中);


2.除了基本数据类型之外的对象,JVM会在堆中创建对象,对象的引用存于虚拟机栈中的局部变量表中


3.并不是所有的对象都在堆中存储,可以走栈上分配,在不在栈上分配取决于Hotspot的一个优化技术:“逃逸分析”


一般JVM 执行某个方法的频次比较高的时候 才会触发逃逸分析,如果不满足方法逃逸就会在栈上分配


第三点讲的比较复杂,我们只要知道堆内存用来存放由 new 创建的对象和数组,在堆中分配的内存,由 Java 虚拟机的自动垃圾回收器来管理。在堆中产生了一个数组或者对象之后,还可以在栈中定义一个特殊的变量,让栈中的这个变量的取值等于数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量,以后就可以在程序中使用栈中的引用变量来访问堆中的数组或者对象,引用变量就相当于是为数组或者对象起的一个名称。引用变量是普通的变量,定义时在栈中分配,引用变量在程序运行到其作用域之外后被释放。而数组和对象本身在堆中分配,即使程序运行到使用 new 产生数组或者对象的语句所在的代码块之外,数组和对象本身占据的内存不会被释放,数组和对象在没有引用变量指向它的时候,才变为垃圾,不能在被使用,但仍然占据内存空间不放,在随后的一个不确定的时间被垃圾回收器收走(释放掉)。另外,栈中的变量是指向堆内存中的变量的。


概括来说:

我们new生成的对象是存放在堆中的,直接定义的是存放在栈中的。


关于堆和栈到底是什么,我们先不做研究,只需要知道,它们是内存中的两种分区。


3:类方法


一个类也可以有多个方法,

方法分类:

1、 普通

定义普通的方法


格式:


 

访问修饰符[修饰符]返回值方法名(参数。。。)
  {
  方法体。

}


访问修饰符 public default

返回值:void表示没有返回值 数据类型:表示有返回值

方法名:驼峰命名法

参数:可以是多个,也可以没有参数定义类似声明变量 int a,int b

返回值的精度必须够,必须与return一致或比传入的参数都大

接收返回值精度也要够。


2、 静态


1、使用static修饰的变量称作静态变量如:static int a=5;如果变量时非静态的。普通方法是可以调用的,但是静态方法调用就会报错。


2、静态变量可以非静态方法里使用普通方法是可以调用的,而静态方法调用也可以。


3、静态变量可以声明在类中作为成员变量。但是如果声明在方法中作为局部变量则该方法要是静态方法才可以。


3、 私有

用private修饰,访问范围只限于当前类中。

例private String sex ….私有的。


两种方法给private赋值

1、 在原来的类里定义一个方法,直接调用这个方法给它赋值

缺点:只能实现对私有属性赋值,但是没有办法取值。

2、 在原类里重载一个还有private参数的方法。

通过set,get方法实现赋值和取值。

getXXX(String xx)à表示赋值外界的参数给类中私有属性赋值,通过该方法的参数赋值getXXX表示取值获取私有属性的值,而只是通过返回值传递的。


4、 构造

Name name=new Name(); 加了一个()

系统自动给你加下面的这段


Public Bear()
{
}

初始化方法

如果类中没有定义构造方法,则系统会产生一个默认的构造方法。

该构造方法就是无参数构造方法。

如果自己定义了构造方法,则系统不再产生默认的构造方法。


格式:


 

访问修饰符类名(参数)
  {
       //方法体


访问修饰符:public private protected default



还有就是关键词,static,abstract。


其实根据这两个也可以分类方法,用static 修饰的叫做静态方法,没有的则叫非静态方法。


静态方法是可以直接访问而不是实例化自身访问。静态方法只需要初始化一次。


我们来看具体代码


下面展示一些 内联代码片。


package student;
public class Jgdabc {
  public static void introduce() {
  System.out.println("大家好,我是jgdabc");
  }
  public static void main(String args[]) {
  Jgdabc.introduce();//直接使用类名来调用静态方法
  //通过对象名调用,需要实例化对象
  Jgdabc jgdabc = new Jgdabc();
  jgdabc.introduce();
}
}


静态方法中可以直接调用同类中的静态成员,但是不可以调用非静态成员。至于机理,我们这样去理解,非静态方法/变量只有实例化之后才被存放入堆的。没有实例化只会在栈内存,静态方法与变量在堆里。我是这样理解的,但其实你看网上会说有关方法区的相关。但其实你看之后其实方法区是被取代了的,JDK8之后,永久代被移除了,取而代之的是元空间(metaspace)。但元空间中存储的主要是.class文件的元数据信息,静态成员的存储位置由方法区转到了堆内存(heap)中。



好了,不往下谈了。涉及太多,多理解内存的存储机制,还有看看java虚拟机也许就好理解了。如果我总结的有错,还望大佬指正。

我们来试着写一段代码,来证明一下。



可以看到,eclipse中,我们introduce调用非静态的age,是报错的,而调用静态的变量name是不报错的。

同理,静态方法中不能直接调用非静态方法,需要通过对象来访问

非静态方法。看,报错。



我们这样来做




普通方法是需要通过对象来调用的,静态方法可以直接调用。当然,你也可以通过对象调用。如下:



非静态成员方法/变量都是必须依赖具体的对象才能够被调用。

另外我们也可以根据是否有参数和是否有返回值,我们可以将方法再一次分类。

无参无返回值的方法。

无参有返回值的方法。

有参无返回值的方法。

有参有返回值的方法。

我们详细说说构造方法(重点)

我们也可以将构造方法分类:

无参构造方法:

public Dog(){} //如果一个类没有定义构造方法,则默认无无参构造,如果有定义有参构造,最好再显示定义一个无参构造方法

带参构造方法:


public Dog(String name){
this.name = name;
}


多参构造方法:

public Dog(String name,int age){
this.name = name;
this.age = age;
}

我们需要注意这些:

(1)构造方法名称与类名相同,没有返回值声明(包括 void)

(2)构造方法用于初始化数据(属性)

(3)每一个类中都会有一个默认的无参的构造方法

(4)如果类中有显示的构造方法,那么默认构造方法将无效

(5)如果有显示的构造方法,还想保留默认构造 方法,需要显示的写出来。

(6)构造方法可以有多个,但参数不一样,称为构造方法的重载

(7)在构造方法中调用另一个构造方法,使用this(…),该句代码必须在第一句。

(8)构造方法之间的调用,必须要有出口。

(9)给对象初始化数据可以使用构造方法或setter方法,通常情况下,两者都会保留。

(10)一个好的编程习惯是要保留默认的构造方法。(为了方便一些框架代码使用反射来创建对象)

(11)private Dog(){},构造方法私有化,当我们的需求是为了 保正该类只有一个对象时(单例模式就是私有化构造器)。


我们来看几个案例


1:运用构造方法及构造方法重载,实现不同变量初始化的过程。

要求:1、创建学生类Student(1)在类中定义成员变量:name,no,sex,address(分别描述学生的姓名,学号,性别,地址信息);(2)在类中定义成员方法 show:实现输出所有成员变量的信息。(3)在类中定义构造方法:分别将2个成员变量,3个成员变量,4个成员变量初始化。2、创建测试类TestStudent(1)在类中创建3个同学对象。(2)分别对3个对象运用3个构造方法赋值(3)输出每个同学对象的信息。

代码实现:



注意这里的this 关键字

this关键字指向的是当前对象的引用

调用类中的属性:this.属性名称,指的是访问类中的成员变量,用来区分成员变量和局部变量(重名问题)

调用类中的方法:this.方法名称,用来访问本类的成员方法

调用类构造方法:this();访问本类的构造方法,()中可以有参数的 如果有参数 就是调用指定的有参构造

注意:

1.this() 不能使用在普通方法中,只能写在构造方法中

2.必须是构造方法中的第一条语句

2:

定义一个Person类,要求:1、在类中定义两个String类型的成员变量name和nationality(国籍),变量nationality使用static关键字修饰为静态变量。2、定义多个静态方法,描述Person类的行为,例如:睡觉,吃饭。在静态方法之间互相调用。3、定义一个静态代码块,在代码块中为静态变量nationality赋值为“中国”。定义一个测试类TestPerson,要求:1、在类的main()方法中创建Person类的两个实例对象,并输出这两个对象的姓名和国籍。2、用不同的方法调用Person类的成员方法。





4:静态代码块


这里有涉及与静态代码块相关的概念。


①、格式

 在java类中(方法中不能存在静态代码块)使用static关键字和{}声明的代码块:

public class CodeBlock {
static{
System.out.println(“静态代码块”);
}
}


②、执行时机

 静态代码块在类被加载的时候就运行了,而且只运行一次,并且优先于各种代码块以及构造函数。如果一个类中有多个静态代码块,会按照书写顺序依次执行。后面在比较的时候会通过具体实例来证明。


 ③、静态代码块的作用

 一般情况下,如果有些代码需要在项目启动的时候就执行,这时候就需要静态代码块。比如一个项目启动需要加载的很多配置文件等资源,我们就可以都放入静态代码块中。


 ④、静态代码块不能存在任何方法体中


 这个应该很好理解,首先我们要明确静态代码块是在类加载的时候就要运行了。我们分情况讨论:

 对于普通方法,由于普通方法是通过加载类,然后new出实例化对象,通过对象才能运行这个方法,而静态代码块只需要加载类之后就能运行了。

 对于静态方法,在类加载的时候,静态方法也已经加载了,但是我们必须要通过类名或者对象名才能访问,也就是说相比于静态代码块,静态代码块是主动运行的,而静态方法是被动运行的。

 不管是哪种方法,我们需要明确静态代码块的存在在类加载的时候就自动运行了,而放在不管是普通方法还是静态方法中,都是不能自动运行的。


 ⑤、静态代码块不能访问普通变量

 这个理解思维同上,普通变量只能通过对象来调用,是不能放在静态代码块中的。**


5:构造代码块

另外其实还有构造代码块


①、格式:在java类中使用{}声明的代码块(和静态代码块的区别是少了static关键字):

下面展示一些 内联代码片。


public class CodeBlock {
    static{
        System.out.println("静态代码块");
    }
    {
        System.out.println("构造代码块");
    }
}


②、执行时机


 构造代码块在创建对象时被调用,每次创建对象都会调用一次,但是优先于构造函数执行。需要注意的是,听名字我们就知道,构造代码块不是优先于构造函数执行,而是依托于构造函数,也就是说,如果你不实例化对象,构造代码块是不会执行的。怎么理解呢?我们看看下面这段代码:



 和构造函数的作用类似,都能对对象进行初始化,并且只要创建一个对象,构造代码块都会执行一次。但是反过来,构造函数则不一定每个对象建立时都执行(多个构造函数情况下,建立对象时传入的参数不同则初始化使用对应的构造函数)。


 利用每次创建对象的时候都会提前调用一次构造代码块特性,我们可以做诸如统计创建对象的次数等功能。


6:普通代码块

普通代码块和构造代码块的区别是,构造代码块是在类中定义的,而普通代码块是在方法体中定义的。且普通代码块的执行顺序和书写顺序一致。

下面展示一些 内联代码片。


public void sayHello(){
    {
        System.out.println("普通代码块");
    }
}


7:代码块的执行顺序


静态代码块>构造代码块>构造函数>普通代码块 

下面展示一些 内联代码片。


public class CodeBlock {
    static{
        System.out.println("静态代码块");
    }
    {
        System.out.println("构造代码块");
    }
    public CodeBlock(){
        System.out.println("无参构造函数");
    }
    public void sayHello(){
        {
            System.out.println("普通代码块");
        }
    }
    public static void main(String[] args) {
        System.out.println("执行了main方法");
        new CodeBlock().sayHello();;
        System.out.println("---------------");
        new CodeBlock().sayHello();;
    }
}


输出



相关文章
|
19天前
|
存储 Java
Java中判断一个对象是否是空内容
在 Java 中,不同类型的对象其“空内容”的定义和判断方式各异。对于基本数据类型的包装类,空指对象引用为 null;字符串的空包括 null、长度为 0 或仅含空白字符,可通过 length() 和 trim() 判断;集合类通过 isEmpty() 方法检查是否无元素;数组的空则指引用为 null 或长度为 0。
|
2月前
|
Java
Java快速入门之类、对象、方法
本文简要介绍了Java快速入门中的类、对象和方法。首先,解释了类和对象的概念,类是对象的抽象,对象是类的具体实例。接着,阐述了类的定义和组成,包括属性和行为,并展示了如何创建和使用对象。然后,讨论了成员变量与局部变量的区别,强调了封装的重要性,通过`private`关键字隐藏数据并提供`get/set`方法访问。最后,介绍了构造方法的定义和重载,以及标准类的制作规范,帮助初学者理解如何构建完整的Java类。
|
2月前
|
安全 Java
Object取值转java对象
通过本文的介绍,我们了解了几种将 `Object`类型转换为Java对象的方法,包括强制类型转换、使用 `instanceof`检查类型和泛型方法等。此外,还探讨了在集合、反射和序列化等常见场景中的应用。掌握这些方法和技巧,有助于编写更健壮和类型安全的Java代码。
50 17
|
2月前
|
Java
java代码优化:判断内聚到实体对象中和构造上下文对象传递参数
通过两个常见的java后端实例场景探讨代码优化,代码不是优化出来的,而是设计出来的,我们永远不可能有专门的时间去做代码优化,优化和设计在平时
37 15
|
3月前
|
Java
java中面向过程和面向对象区别?
java中面向过程和面向对象区别?
39 1
|
4月前
|
JavaScript 前端开发 Java
还不明白面向对象? 本文带你彻底搞懂面向对象的三大特征(2024年11月Java版)
欢迎来到我的博客,我是瑞雨溪,一名热爱JavaScript和Vue的大一学生。如果你从我的文章中受益,欢迎关注我,我将持续更新更多优质内容。你的支持是我前进的动力!🎉🎉🎉
38 0
还不明白面向对象? 本文带你彻底搞懂面向对象的三大特征(2024年11月Java版)
|
4天前
|
存储 监控 Java
【Java并发】【线程池】带你从0-1入门线程池
欢迎来到我的技术博客!我是一名热爱编程的开发者,梦想是编写高端CRUD应用。2025年我正在沉淀中,博客更新速度加快,期待与你一起成长。 线程池是一种复用线程资源的机制,通过预先创建一定数量的线程并管理其生命周期,避免频繁创建/销毁线程带来的性能开销。它解决了线程创建成本高、资源耗尽风险、响应速度慢和任务执行缺乏管理等问题。
104 60
【Java并发】【线程池】带你从0-1入门线程池
|
15天前
|
Java 程序员 开发者
Java社招面试题:一个线程运行时发生异常会怎样?
大家好,我是小米。今天分享一个经典的 Java 面试题:线程运行时发生异常,程序会怎样处理?此问题考察 Java 线程和异常处理机制的理解。线程发生异常,默认会导致线程终止,但可以通过 try-catch 捕获并处理,避免影响其他线程。未捕获的异常可通过 Thread.UncaughtExceptionHandler 处理。线程池中的异常会被自动处理,不影响任务执行。希望这篇文章能帮助你深入理解 Java 线程异常处理机制,为面试做好准备。如果你觉得有帮助,欢迎收藏、转发!
75 14
|
18天前
|
安全 Java 程序员
Java 面试必问!线程构造方法和静态块的执行线程到底是谁?
大家好,我是小米。今天聊聊Java多线程面试题:线程类的构造方法和静态块是由哪个线程调用的?构造方法由创建线程实例的主线程调用,静态块在类加载时由主线程调用。理解这些细节有助于掌握Java多线程机制。下期再见! 简介: 本文通过一个常见的Java多线程面试题,详细讲解了线程类的构造方法和静态块是由哪个线程调用的。构造方法由创建线程实例的主线程调用,静态块在类加载时由主线程调用。理解这些细节对掌握Java多线程编程至关重要。
49 13
|
19天前
|
安全 Java 开发者
【JAVA】封装多线程原理
Java 中的多线程封装旨在简化使用、提高安全性和增强可维护性。通过抽象和隐藏底层细节,提供简洁接口。常见封装方式包括基于 Runnable 和 Callable 接口的任务封装,以及线程池的封装。Runnable 适用于无返回值任务,Callable 支持有返回值任务。线程池(如 ExecutorService)则用于管理和复用线程,减少性能开销。示例代码展示了如何实现这些封装,使多线程编程更加高效和安全。

热门文章

最新文章