面向对象思想
- 面向过程:面向着具体的每一个步骤和过程,把每一个步骤和过程完成,然后由这些功能方法相互调用,完成需求。例如杨辉三角型,打印图形、输入元素每一大步如何做都需要一步一步写出来;
- 面向对象:封装过程,按照需求调用功能;
面向对象的理解:
- 把需求步骤和功能再进行封装,封装时根据不同的功能,进行不同的封装,功能类似的封装在一起。
这样结构就清晰了很多。用的时候,找到对应的类就可以了。这就是面向对象的思想。 - 面对对象的程序是由类实例化对象组成的, 每个对象包含对用户公开的特定功能部分和隐藏的实现部分。程序中的很多对象来自标准库,还有一些是自定义的。
- 类体现封装的思想,封装成员属性和成员功能,例如创造一个机器人类,它需要皮肤(成员)、设置皮肤的颜色(成员属性),会运动(功能);
类与对象
描述现实世界事物:
- 学生这个事物:
属性: 姓名 , 年龄 , 性别 …
功能: 学习 , 吃饭 , 睡觉 …
学习编程语言,就是为了模拟现实中的事物,我们学习的Java语言最基本单位是类,所以,我们就应该把事物用一个类来体现。
-
现实中的事物和类的对应关系:
事物 类 属性 ----------------- 成员变量 行为 ----------------- 成员方法
-
再举例:手机这个类就有属性(颜色、大小)和功能(玩游戏),手机中的成员可以为屏幕、键盘,屏幕的颜色就是成员属性。
定义类:就是定义类的成员(成员变量和成员方法)
a:成员变量 和以前定义变量是一样的,只不过位置发生了改变。在类中,方法外。
b:成员方法 和以前定义方法是一样的,只不过把static去掉,后面在详细讲解static的作用。
类和对象的概念
a:类:是一组相关的属性和行为的集合
b:对象:是该类事物的具体体现
c:举例:
类 学生
对象 班长就是一个对象
- 封装是与对象有关的一个重要概念。从形式上看,封装就是
将数据和行为组合在一个包中, 并对对象的使用者隐藏了数据的实现方式
。实现封装的关键在于绝对不能让类中的方法直接地访问其他类的实例域。程序仅通过对象的方法与对象数据
进行交互。封装给对象赋予了“ 黑盒” 特征, 这是提高重用性和可靠性的关键。 这意味着一个类可以全面地改变存储数据的方式,只要仍旧使用同样的方法操作数据, 其他对象就不会知道或介意所发生的变化。OOP 的另一个原则会让用户自定义 Java 类变得轻而易举,在 Java 中, 所有的类都源自于一个"神通广大的超类"Object。
类的使用
-
创建对象格式:
类名 对象名 = new 类名();
-
使用成员变量格式:
对象名.变量名
-
使用成员方法格式:
对象名.方法名(...)
-
main这个类方法是程序运行的入口,用来调用别的类,因此调用类是在程序的main方法下执行的
方法区先加载 Test.class文件,然后读取main方法来执行程序,调用其他的类等等。 -
使用类时,在一个java文件夹(package)中写两个类:一个基本的自定义类,一个测试类。测试类用来调用自定义的类,在main方法下执行。
-
多个类可以并列定义到一个java文件中,但是
public
只能加给带有main方法,作为入口的类。
建议一个类就是一个java文件
举例:自定义一个手机类如下:
public class Phone {
// 定义成员变量
String name="小米";
String color="白色";
double price=799;
//定义成员方法
public void call(String name){
System.out.println("给"+name+"打电话");
}
public void sendMsg(String name,String content) {
System.out.println("给"+name+"发了短信:"+content);
}
}
测试类如下:
public class MyTest {
public static void main(String[] args) {
//创建Phone类的对象
Phone phone = new Phone();
//获取成员变量的值
String name = phone.name;
String color = phone.color;
double price = phone.price;
System.out.println(name);
System.out.println(color);
System.out.println(price);
// 打印对象地址值
System.out.println(phone);
//调用成员方法
phone.call("王祖贤");
phone.sendMsg("林青霞","很想你");
}
}
创建一个对象的内存图解:
创建一个对象分析: Student s = new Student();
内存步骤如下:
(1):加载Student.class文件进内存
(2):在栈内存为s开辟空间
(3):在堆内存为学生对象开辟空间
(4):对学生对象的成员变量进行默认初始化
(5):对学生对象的成员变量进行显示初始化
(6):通过构造方法对学生对象的成员变量赋值
(7):学生对象初始化完毕,把对象地址赋值给s变量
创建多个对象分析:
- 一个对象内存图:栈内程序调用什么类,类的字节码文件就进方法区,调用main方法,main方法进栈执行程序,堆中实例化对象,将类的成员变量拷贝一份进行默认初始化,如果类方法中定义了值会迅速覆盖默认值,返回地址值给栈中对象名。方法在方法区中,栈中加载类方法与自定义方法执行,执行完后弹栈。
- 2个对象的内存图:每new一次会开辟新的内存空间,将类的成员变量拷贝一份进行默认初始化,如果类方法中定义了值会迅速覆盖默认值,返回地址值给栈中对象名。接下来栈中按照程序执行。
- 3个对象的内存图:如果第三个对象为
Person p1=p;
表示将p的地址值赋给p1,p1与p指向同一个地址,两个对象的属性值与方法均相等。没有new不会在堆中开辟新的内存空间
局部变量和成员变量的区别
A:在类中的位置不同
成员变量:在类中方法外,new对象时在堆内才会初始化类的成员变量,因此成员变量属于对象。
局部变量:在方法定义内部中或者方法声明(形参)上,属于方法。
B:在内存中的位置不同
成员变量:创建对象,在堆内拷贝成员变量并赋默认值,因此在堆内存储
局部变量:方法加载进栈,在栈内存储
C:生命周期不同
成员变量:随着对象的创建而存在,随着对象的消失而消失
局部变量:随着方法的调用而存在,随着方法的调用完毕而消失
D:初始化值不同
成员变量:有默认初始化值,创建对象时在堆内拷贝成员变量并赋默认值
局部变量:没有默认初始化值,必须定义,赋值,然后才能使用。
注意事项:
- 局部变量名称可以和成员变量名称同名时,在方法中使用的时候,采用的是就近原则;
先在方法内部局部范围找变量;没有找到去成员范围找,找到就使用。没有找到就报错。
举例:
自定义一个类
//定义一个人的类 没有static
public class Person {
//成员变量,对象属性
int age;
String name="scq";
// 类方法
public void age() {
//age()没有传参,此age为成员范围的对象属性age变量,重新修改成员变量
age=40;
System.out.println(age);
}
public void show(int age) {
//局部范围没有定义name的值,去成员范围找
System.out.println(name); //scq
System.out.println(age); //打印传入的参数age
name="就近原则";
System.out.println(name);//就近原则
//age是局部变量,show调用完释放age
age=100;//修改局部变量age
System.out.println(age);//100
}
}
测试类:
public class Test {
public static void main(String[] args) {
Person p=new Person();
p.age=3;
System.out.println(p.age);//3
p.age();//40 调用方法,修改成员变量p.age值
//show方法中修改了局部变量age,与外部成员变量p.age无关
System.out.println(p.age);//40
//show的型参为age,与main方法的age无关
p.show(20); //scq
//就近原则
// 20
//show方法中修改了局部变量age,与外部成员变量p.age无关
System.out.println(p.age);//40
}
}
方法的形式参数是类名时如何调用
- Java中参数传递问题
形式参数- 基本类型:属于值传递,形式参数的改变不改变实际参数
- 引用类型:属于引用传递,传递的是地址值,形式参数的改变直接改变实际参数
- 方法的参数是类名
如果你看到了一个方法的形式参数是一个类 类型(引用类型),这里其实需要的是该类的对象。就是将该类的对象地址值传入方法,当方法内部修改该对象的成员变量就是直接修改。
举例:
自定义一个类
public class Student{
//student类对象的属性num初始化为20
int num=20;
}
测试类:
public class Test {
public static void main(String[] args) {
// 成员变量num
int num=10;
Student student=new Student();
//形参为类的类型时,传递该类的对象。类是引用数据类型,传递地址值,形参改变影响实参。
// test方法用来修改对象属性值。
test(student,num);
System.out.println(student.num);//30
//test中的局部变量num和成员变量num同名时:test内部的局部变量num在test方法调用完就是释放
//基本数据类型作为参数传递时传递值,局部变量num的改变不影响成员变量num
System.out.println(num);//10
}
//定义一个test方法修改对象属性 //局部变量num
public static void test(Student student,int num) {
num+=20;
//形参为对象,传入地址值,对student的num值重新赋值会直接修改student的num值
student.num=num;
}
}
图解:
匿名对象的概述和应用
匿名对象:
- 就是没有名字的对象;
- 表示堆内新建的对象没有把地址值赋给引用;
- 如果当做实参传入自定义方法,传入的是匿名对象的地址值
创建对象格式:类名 对象名 = new 类名();
创建匿名对象的格式:new 类名();
没有东西接收这个对象。因此堆内新建的对象也无法把地址值给引用。
匿名对象的应用:
- 因为每new一次都是一个新的对象,所以调用方法,仅仅只调用一次的时候使用匿名对象。
- 匿名对象可以作为实际参数传递
举例:
自定义一个类
public class Dog{
int num=100;
public void eat() {
System.out.println("eating");
}
}
测试类:
public class Test {
public static void main(String[] args) {
//new Dog()可以直接调属性方法和类方法
System.out.println(new Dog().num);
//两个匿名对象各调用了一次,每new一次就是一个新的内存空间,地址值不同。
//因此 只调用方法一次的时候,就可以使用匿名对象来调用。
// 如果要使用匿名对象的属性只能使用一次:使用方法为:new Dog().num
new Dog().eat();
new Dog().eat();
//如果要一次修改匿名对象的属性值,方法如下:
new Dog().num=10;
//同一个对象 调用了eat()方法两次
Dog dog=new Dog();
dog.eat();
dog.eat();
//匿名对象 作为参数传入方法
// 由于每new一次就是一个新的对象。当做实参传入自定义方法,传入的是匿名对象的地址值,并且修改了这个地址值内所对应对象的num的属性值,然后test方法执行结束,弹栈。
test(new Dog(),20);//new Dog()开辟内存空间后,传入匿名对象的地址值
//Dog对象自身属性num初始值为100,test将num重新赋值 ,因为对象为引用数据类型,作为参数传递传入地址值,形参会改变实参。因此dog.num会重新赋值为30
test(dog,30);
System.out.println(dog.num);//30
//打印一个新的匿名对象 的属性值num ,没有赋值改变,因此为初始值100
System.out.println(new Dog().num);
}
//定义一个传入对象修改对象属性的test方法
public static void test(Dog dog,int num) {
dog.num=num;
}
}
图解:
private关键字的概述和特点
我们采用对象名.成员变量
这种方式可以给成员变量设置值,但有时候类里面的成员变量需要有一些限制(如果用户输入年龄为230),如何屏蔽这种设置?
- 由于主方法内实例化对象后对成员变量直接赋值、直接操作成员方法不安全,因此给出权限修饰符;
- 我们可以使用关键字private,private是一个权限修饰符,可以修饰成员变量和成员方法,不可以修饰类。被修饰的成员只能在本类当中被访问。出了自定义类的范围,主方法(外界)无法访问到成员变量,也无法赋值,例如主方法内:
phone.color="black";
- 外界可以通过对象调用类方法来赋值和获取加了private的成员变量,而类方法中加入一定条件外界才可以成功为成员变量赋值。更加安全。
- 私有变量,不可以在方法中当做形参传入,在方法中当做形参传入就是在对象调用时要传入的参数,此时表示对象可以访问这些私有变量,那么私有变量就毫无意义。
public class Rectangle {
/* 定义一个长方形(Rectangle)类,定义求周长(length)和面积(area)的方法,
然后定义一个测试类Test,进行测试。*/
private double length1;
private double length2;
public Rectangle() {
}
//double可以在调用构造方法时输入int型 Double只能输入浮点型
public Rectangle(double length1, double length2) {
this.length1 = length1;
this.length2 = length2;
}
public Double getLength1() {
return length1;
}
public void setLength1(double length1) {
this.length1 = length1;
}
public Double getLength2() {
return length2;
}
public void setLength2(double length2) {
this.length2 = length2;
}
//长宽是私有变量,不可以在方法中当做形参传入,在方法中当做形参传入就是在对象调用时要传入的参数,此时表示对象可以访问这些私有变量,那么私有变量就毫无意义。
public void getLength(){
System.out.println((length1 + length2) * 2);
}
public void getArea(){
System.out.println(length2 * length1);
}
测试类:
public class mainmain {
public static void main(String[] args) {
Rectangle rectangle = new Rectangle(3,2);
rectangle.getLength();
rectangle.getArea();
- 权限修饰符
范围:public > protected> 缺省的 (不写权限修饰符)> private
- 类可以用public 和 缺省来修饰,成员变量和成员方法都可以权限修饰符被修饰,类如果想被不同包下调到 不能缺省要加上
public
,不同包下只有public修饰的方法和成员变量可以访问; - public范围最大,在任何地方都可以访问到。可以修饰类、成员变量、成员方法;
this关键字的概述和应用
当局部变量和成员变量名字相同的时候,如果我们不使用this关键字,局部变量会在自己的作用域内根据就近原则屏蔽了成员变量;也就是说当调用set方法时,形参名称与成员变量重名
,按照就近原则无法找到成员变量,而为了区分成员变量和局部变量,使用this来区分
- this关键字代表当前类的对象地址值,谁调用这个成员方法,那么该方法的内部的this就代表谁。使用
this.变量名
就可以在成员方法内部屏蔽局部变量找到成员方法外部的成员变量。 - this的作用:解决局部变量隐藏成员变量
举例:
自定义一个类
public class Phone {
int name; //缺省
private String color;
private double money;
public void show(){
//没有设置属性值之前,在实例化对象的时候堆会初始化默认值money为0.0,color为null
System.out.println(money);
System.out.println(color);
}
//提供public(类内部和外界都可以访问)的set/get方法,让外界通过调用方法给成员变量设置值和获取成员变量值。
public void setMoney(double money) {
if (money>=1000)
this.money = money;
}
public double getMoney(){
//只有对象才能调用属性,某个对象调用这个成员方法,传入这个对象给this来调用这个对象的属性money
//下面不在成员变量money中加入this是因为此函数没有与成员变量相同的局部变量,因此按照就近原则可以找到方法外部的成员变量money,不需要加this区分。此时money前面有默认的this。
return money;//就是 return this.money
}
public void setColor(String color){
//按照就近原则,左边的color找传入的形参变量color,右边的color为传入值形参color,因此为自己赋值给自己。并没有将形参color给成员变量color赋值
color=color;
//this:类实例化对象的地址值,哪个对象调用这个方法,方法中的this就代表这个对象。
//通过对象.属性 , 找到成员变量实现为成员变量赋值
this.color=color;
}
public String getColor() {
this.call(); //call()为私有方法,外界无法调用,但是类内部可以调用
return color; //此处省略了this.color直接用color,没有局部变量与成员变量重名,因此不影响,不需要用this来区分。
}
//私有方法,外界无法调用,但是类内部可以调用
private void call(){
System.out.println("call");
}
}
测试类:
Phone phone=new Phone();
//没有设置属性值之前,在实例化对象的时候堆会创建内存空间拷贝类的成员变量并初始化默认值,money为0.0,color为null
phone.show();// 0.0 null
phone.setMoney(500);
double money= phone.getMoney();
System.out.println(money);//0.0
phone.setColor("red");
//对于有返回值的方法可以直接打印
System.out.println(phone.getColor());//call red
构造方法
构造方法概述:
- 在创建对象同时,构造方法会运行,以便将实例域初始化为所希望的状态;
- 构造方法与其他的方法有一个重要的不同:构造器总是伴随着 new 操作符的执行被调用;
- 每个类都有一个默认的无参的构造方法,但是如果你定义了有参的构造方法,这个无参的构造方法就不见了,如果你需要这个无参的构造方法,你需要自己手动添加;
- 不写构造方法默认带有空参构造。
- 构造方法可以重载,可以同时在类中写有参与无参的构造方法,因此每个类可以有一个以上的构造方法
- 构造方法可以有 0 个、1 个或多个参数
构造方法作用:
- 有了构造方法才可以对类进行实例化。
- 创建对象 , 可以通过有参构造方法来为成员变量赋值,无需set方法,更加方便。
构造方法格式:
- :方法名与类名相同
- :没有返回值类型,连void都没有
- :没有具体的返回值
//使用关键字new 来调用构造方法实例化对象
Student student = new Student();
一个标准的定义类的要求:
私有化成员变量,提供get set方法,写空参和有参的构造方法
举例:
定义一个标准的类
public class Student {
private String name;
private int age;
//重载构造方法,定义有参与无参的构造方法
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
测试这个类:
public class mainmain {
public static void main(String[] args) {
//借助空参构造方法创建对象,调用set方法给成员变量赋值
Student student1 = new Student();
student1.setAge(20);
System.out.println(student1.getAge());//20
//借助有参构造方法创建对象,同时给对象的成员变量赋值,常用比较方便
Student student2 = new Student("scq", 22);
System.out.println(student2.getAge());//22
System.out.println(student2.getName());//scq
}
}