作者:~小明学编程
文章专栏:JavaSE基础
格言:目之所及皆为回忆,心之所想皆为过往。
目录
Java是一门纯面向对象的语言(Object Oriented Program,继承OOP),在面向对象的世界里,一切皆为对象。面向对象是解决问题的一种思想,主要依靠对象之间的交互完成一件事情。用面向对象的思想来涉及程序,更符合人们对事物的认知,对于大型程序的设计、扩展以及维护都非常友好。
面向对象是只关注我们的结果,而面向过程顾名思义就是注重过程,讲究的是一步一步实现,而面向对象则是封装好的东西我们直接拿来用就可以了。
类是用来对一个实体(对象)来进行描述的,主要描述该实体(对象)具有哪些属性(外观尺寸等),哪些功能(用来干啥),描述完成后计算机就可以识别了。
比如:洗衣机,它是一个品牌,在Java中可以将其看成是一个类别。
属性:产品品牌,型号,产品重量,外观尺寸,颜色...
功能:洗衣,烘干、定时
在java中定义类时需要用到class关键字,具体语法如下
- // 创建类
- class ClassName{
- field; // 字段(属性) 或者 成员变量
- method; // 行为 或者 成员方法
- }
class为定义类的关键字,ClassName为类的名字,{}中为类的主体。
类中包含的内容称为类的成员。属性主要是用来描述类的,称之为类的成员属性或者类成员变量。方法主要说明类具有哪些功能,称为类的成员方法。
- class Person {
- public String name;
- public int age;
- public void eat() {
- System.out.println(name+"在吃饭……");
- }
- public void print() {
- System.out.println("姓名:"+name+" 年龄:"+age);
- }
- }
- public class Dome {
- public static void main(String[] args) {
- Person person = new Person();
- person.name = "张三";
- person.age = 20;
- person.eat();
- person.print();
- }
- }
采用Java语言将洗衣机类在计算机中定义完成,经过javac编译之后形成.class文件,在JVM的基础上计算机就可以识别了。
注意事项
1.类名注意采用大驼峰定义。
2.成员前写法统一为public,后面会详细解释。
3.此处写的方法不带 static 关键字. 后面会详细解释。
内存布局
这里简单给大家介绍一下内存布局我们在主方法中new了一个对象,person是一个变量存放在栈区,它的值是一个地址,我们也把这个变量叫做引用(不是所有的引用都放在栈区,我们类中的成员也可以是一个引用,此时的引用就放在了堆区),person的类型是Person是一个类,我们new出来的是一个新的对象,我们可以通过引用找到这个对象并对其进行各种操作。
我们看一下下面这段代码
- class Person {
- public String name;
- public int age;
- public int count;
- public static int count1;
- public void eat() {
- System.out.println(name+"在吃饭……");
- }
- public void print() {
- System.out.println("姓名:"+name+" 年龄:"+age);
- }
- }
- public class Dome {
-
- public static void main(String[] args) {
- Person person1 = new Person();
- Person person2 = new Person();
- person1.count++;
- person1.count1++;
- System.out.println("count="+person1.count);
- System.out.println("count1="+person1.count1);
- person2.count++;
- person2.count1++;
- System.out.println("count="+person2.count);
- System.out.println("count1="+person2.count1);
-
- }
-
- }
我们在类中定义了一个count和一个count1一个用static修饰了一个没用,然后在主函数中我们定义了两个变量person1和person2,通过前面的学习我们知道我们通过这两个引用,引用它们的对象是互不干扰的,所以我们的count应该都是1。
但是这里我们看到count是1没有错但是count却是有增长,这又是什么原因呢?
想要了解这个问题的话我们首先得知道类在内存中的布局。
通过前面的学习我们知道方法区会存放我们类的代码部分代码片段,还会存放我们的静态成员变量,所以我们的count1不是存放在堆里面的而是放在我们的方法区中的,这就相当于我们在C语言中函数中定义一个静态变量这个count1的值不唯一与person1或者person2有关,而是直接关系着Person,所以我们一般就直接写成这种格式,也就是直接通过类找到这个变量,也就是说我们静态成员变量是不依赖对象来访问的,我们也可通过类名.静态成员变量来访问。
Person.count1++;
同理我们的在方法前面加static也是这么一个道理。
- class Person {
- public String name;
- public int age;
- public int count;
- public static int count1;
- public void eat() {
- System.out.println(name+"在吃饭……");
- }
- public void print() {
- System.out.println("姓名:"+name+" 年龄:"+age);
- }
- public static void fun() {
- System.out.println("hello world!");
- }
- }
- public class Dome {
- public static void main(String[] args) {
- Person.fun();
- }
- }
直接通过类就可以调用我们的fun方法了。
我们要知道我们的静态成员变量也就是被static修饰的变量是不能定义在方法中的,只能定义在我们的类之中,不然就会报错。
注意
1.static定义的变量,类变量,属于类。
2.eat方法的调用需要对应的引用来调用,但是如果可以定义static的变量,Person就可以调用。
3.静态变量不能在方法中定义。
在Java中我们普通的方法是可以调用static修饰过的静态的方法,但是static修饰的静态方法是不能够调用我们普通的没有被static修饰过的方法的。
- public static void fun() {
- fun1();
- System.out.println("hello world!");
- }
- public void fun1() {
- fun();
- System.out.println("hello");
- }
这里也很好理解,因为我们普通方法是依赖对象的而静态方法是依赖类的,有类我们就能用,所以我们用普通方法的时候肯定有对象,既然有对象也肯定有类,但我们用静态方法的时候只能证明它有类,但不一定有对象,所以我们的静态方法不能直接调用我们的普通方法,同理我们可以知道静态方法也可以调用静态方法。
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。
在认识封装之前我们先看一下下面这段代码
- class Person {
- public int age;
- public String name;
- public void fun() {
- System.out.println("hello");
- }
-
- @Override
- public String toString() {
- return "Person{" +
- "age=" + age +
- ", name='" + name + '\'' +
- '}';
- }
- }
- public class Dome {
- public static void main(String[] args) {
- Person person = new Person();
- person.name = "张三";
- person.age = 18;
- System.out.println(person);
- }
- }
我们在我们的Person类里面定义了一个成员变量name如果我们想要把这个成员变量name改为myname时这时候不仅是我们类里面需要改,我们所有的引用Person类中的name全部都要改为myname如果我们这个类的使用者很多的话,无疑将是一个繁琐的工程,那么如何解决上述问题呢?
- public void setName(String myName) {
- name = myName;
- }
- public String getMyName() {
- return name;
- }
我们可以通过上述的方式,提供两个公开的接口来完成我们对name的赋值和使用,此时我们的name已经被封装起来了不能通过对象来直接调用我们的那么,这时候我们想要把name改为name1就可以直接把类里面的name改为name1就行了。
- class Person {
- public int age;
- private String name1;
- public void fun() {
- System.out.println("hello"+name1);
- }
-
- public void setMyName(String myName) {
- name1 = myName;
- }
- public String getMyName() {
- return name1;
- }
-
- @Override
- public String toString() {
- return "Person{" +
- "age=" + age +
- ", name='" + name1 + '\'' +
- '}';
- }
- }
- public class Dome {
- public static void main(String[] args) {
- Person person = new Person();
- // person.name = "张三";
- person.setMyName("张三");
- person.age = 18;
- System.out.println(person);
- person.fun();
- }
- }
这时我们把name改为name1只需要把类里面的name修改就行了,同时也增强了,我们成员变量name的安全性。
那么当我们的封装的变量过多时怎么办呢?一个一个写接口的话太浪费时间了,这时我们可以通过编译器自动生成接口函数。
第一步:在类中的空白处单击右键找到Generate
第二步:找到Getter和Setter可以按需要只生成Getter或者Setter也行
第三步:选择我们要封装的成员变量
- public int getAge() {
- return age;
- }
-
- public void setAge(int age) {
- this.age = age;
- }
this.age指的是调用该类的成员变量这样就防止了把age误认为局部变量。