1 面向对象的三大特点
封装
继承
多态
1.1 封装
封装是指一种将抽象性函式接口的实现细节部分包装、隐藏起来的方法。
private 关键字 是一个权限修饰符 可以修饰成员(成员变量和成员方法)
作用:
保护成员不被别的类使用(被private修饰的成员 的作用范围仅限于当前类)
针对private所修饰的成员变量,如果需要被其他的类使用 ,提供相应的额外操作方法:
- 提供get变量名() 用户获取成员变量(被private)的值,方法修饰的是public
- 提供set变量名(参数) 方法 用户获取成员变量的值 方法也是必须用public进行修饰
- 例如:
private int age =18;// 在声明成员变量的时候 可以给其赋予一个默认值
public int getAge(){
return age;
}
public void setAge(int a){}
1.2 this 关键字
this修饰成员变量:
可以区分成员变量和局部变量: 如果方法的形参和成员变量同名,不带this修饰的变量指的是形参,而this修饰的是成员变量
例如:
public void setName(String name ){
System.out.println(name);
this.name = name;//将局部变量的name赋值给成员变量的name 可以使用this
}
什么时候使用this? ---- 解决局部变量(隐蔽)成员便量
采用 this 关键字是为了解决成员变量和局部变量之间发生的同名的冲突。
this的内存原理:
this代表当前调用方法的对象。哪个对象调用当前的方法,this就代表哪个对象
标准的Java类的写法(Java bean):
/*
类 所包含的应该是与其相关的成员
*/
public class Student {
//成员变量
private String name;
private int age =18;// 在声明成员变量的时候 可以给其赋予一个默认值
public String getName(){
return name;
}
public void setName(String name ){
this.name = name;//将局部变量的name赋值给成员变量的name 可以使用this
}
public int getAge(){
return age;
}
public void setAge(int age){
if(age < 0 || age > 120){
System.out.println("您输入的年龄不合法");
}else{
this.age = age;
}
}
}
// JavaBean主要用来传递数据,即把一组数据组合成一个JavaBean便于传输。
1.3 封装思想
封装是面向对象的三大特征之一。
是面向对象编程语言对客观世界的模拟,客观世界里成员变量都是隐藏在对象内部的,外界是无法直接操作的。
封装原则:
将类的某些信息隐藏在类的内部,不允许外部程序直接访问。而是通过该类提供的方法来实现对隐藏信息的操作和访问。
成员变量使用private修饰 提供对象的getXxx()和setXxx()方法
封装的好处:
- 通过方法来控制成员变量的操作,提高代码的安全性
- 把代码用方法进行封装,可以提高代码的复用性。
1.4 java中对封装的体现
-
对于成员变量的封装 使用private
-
类体现的也是对一类事物的属性和行为的封装
-
方法体现了封装的思想:就是将某一个功能封装在其中。
2 构造方法
构造方法也称为构造器(constructor),用于对象的初始化。
- 构造方法是一个创建对象时被自动调用的特殊方法。 目的就是对对象进行初始化。
- 构造方法的名称要与类名完全一致。 构造方法没有返回值 (void也不写)
- java通过new关键字来调用构造方法,从而返回该类的一个实例对象,是一种特殊的方法
2.1 构造方法的注意事项
- 构造方法 :
- 如果没有定义构造方法 系统将给出一个默认的无参构造方法
- 如果定义了构造方法 系统都将不再提供默认的无参构造
- 构造方法的重载
- 如果自定义了带参的构造方法,还要使用无参构造,就必须手动写出无参构造
- 在一个类中,与类名相同的方法就是构造方法。每个类可以具有多个构造方法,但要求它们各自包含不同的方法参数。
-
无论是否使用无参构造,在开发中,都推荐显式的将无参构造定义出来
-
重要功能:可以使用带参构造为成员变量进行初始化
示例:
public Student(){
// System.out.println("student的无参构造被调用...");
}
public Student(String name){
this. name = name;
}
public Student(int age){
this.age =age;
}
public Student(String name , int age){
this.name = name;
this.age = age;
}
//可以使用this(参数列表)调用本类的其他构造方法。
//但是要注意一点:使用this调用本类其他构造方法 必须位于构造方法的第一条语句。**因此也就是说对于构造方法的调用只能调用一次。
public Student(String name , int age){// 在构造方法中 去调用别的构造方法
this(name);// 调用本类的构造方法
this.age =age;
}
3 标准的Java bean的写法
-
成员变量 使用private修饰
-
提供getter和seter方法 来访问成员变量
-
提供构造方法:无参的 和带有多个参数的
示例:
public class Book {
//成员变量
private String title;
private double price;
private String author;
//构造方法
public Book(){
}
public Book(String title,double price , String author){
this.title = title;
this.price = price;
this.author = author;
}
//getter 和 sertter
public String getTitle(){
return title;
}
public void setTitle(String title){
this.title = title;
}
public double getPrice(){
return price;
}
public void setPrice(double price){
this.price = price;
}
public String getAuthor(){
return author;
}
public void setAuthor(String author){
this.author =author;
}
}
4 常用API-String
API-application programming interface 应用程序编程接口
Java中的API :
指的是jdk中提供的各种功能的Java类,这些类底层的实现封装了起来,我们不需要关心这些类是如何实现的,只需要学习这些类如何使用即可。
java.lang Class String
String类位于java.lang包下,凡是位于java.lang包下的类,在使用的时候,不需要导包。String类代表的是字符串。java中的所有的字符串都被实现为String类的实例。java程序中所有使用双引号引起来的字符串,都是String类的对象
4.1 String字符串的特点
-
字符串不可改变,他的值在创建之后就不能被更改
-
虽然String的值是不可变的,但是他们是可以被共享。
-
字符串效果上相当于字符数组(char[]),但是底层原理却是字节数组([byte[ ]])
4.2 String类的构造方法
-
String()
初始化新创建的String
对象,使其表示空字符序列。 -
String(char[] value)
分配一个新的String
,以便它表示当前包含在字符数组参数中的字符序列。 -
String(byte[] bytes)
通过使用平台的默认字符集解码指定的字节数组来构造新的String
。 -
String s = “abc” 直接赋值的方式创建字符串abc
public static void main(String[] args) {
String s1 = "aaaa";//直接赋值
String s2 = new String();//创建一个空字符串
char[] c = {'a','b','c'};
String s3 = new String(c);//字符数组
byte[] b = {97,98,99};
String s4 = new String(b);//字节数组
System.out.println("s1=" + s1);//aaaa
System.out.println("s2=" + s2);//null
System.out.println("s3=" + s3);//abc
System.out.println("s4=" + s4);//abc
}
4.3 创建字符串对象的两种方式的区别
4.3.1.通过构造方法创建
使用new创建字符串对象,每一次new都会申请一个内存空间,虽然内容都是相同的,但是所对应的内存地址是不同的。
char[] c = {'a','b','c'};
String s3 = new String(c);//字符数组
String ss3 = new String(c);
上面的代码。jvm会首先创建一个字符数组,然后每一次new 的时候 都会有一个新的地址,只不过s3 和ss3参考的字符串的内容是相同的
4.3.2 直接赋值的方式创建
使用“ ”方式给出的字符串,只要字符序列相同(顺序和大小写)。无论在程序代码中出现几次,jvm都只会建立一个String对象,并在字符串常量池(String pool)中维护
java中的三种常量池:
字符串常量池 class文件常量池 运行时常量池
- 字符串常量池(又称为全局字符串池)。
jvm所使用的内存中,字符串作为一种特殊的数据类型,占据了大量的内存,且字符串有着大量的重复,由于字符串本身具有不可变性,因此使用字符串常量池对于同样的字符串可以起到一个缓存的作用,防止多次内存分配,从而提供内存的利用率
- 运行时常量池
当程序运行到某个类的时候,class文件中的信息就会被解析到内存的方法区的运行时常量池中,每个类都有一个运行时常量池。
- class文件常量池
class常量池是在编译后每个class文件都有的,class文件中除了包含类的版本 字段 方法 接口等描述信息,还有一项信息就是常量池,用于存放编译器生成的各种字面量和符号的引用。
5 JVM内存模型
jvm从大的方面分为:堆区 栈区 方法区
栈区: 细分为java虚拟机栈 和本地方法栈
堆内存可以划分为 新生代 老年代
新生代: Eden区 from区 to区
划分出来的各个区域 分别保存不同的数据 并且在一般情况下 期采取的内存回收策略也是不同。
5.1. 堆内存
堆内存是jvm内存模型中最大的一块区域,被所有线程共享。是在jvm启动的时候进行创建。几乎所有的对象的空间分配都是在堆区来进行分配。
考虑到jvm的内存回收机制,堆内存划分为新生代和老年代两个区域(默认的新生代和老年代的空间大小比例为1:2).新生代可以在划分为Eden区,From 区(s0) to区(s1),三者的比例(8:1:1) 几乎所有的新对象的创建都是在eden区 完成。在垃圾回收(GC)过程中,Eden区的活跃的对象会被迁移到Survior区,当在达到一定的年龄(经过一定的垃圾回收算法测处理),会被转移到老年代中。
堆内存在物理上可以使不连续,但是在逻辑上需要满足连续。在实现时。可以实现成固定大小的,也可以是可扩展的。
5.2.方法区
方法区又被称为永久代,我们所讲的模型是(Hotsport最常见的jvm的模型)。同样 也是被所有的线程所共享的,该区域主要保存类的信息,静态变量,常量和编译器编译后的代码等数据
jdk7中,将永久代中的字符串常量池移动到堆中。jdk8 撤销了永久代 ,引入了元空间
方法区不需要连续的内存,可以选择固定大小或者可扩展,并且还可以选择不实现垃圾回收,相对而言,垃圾收集行为在这个区域是比较少见的,但并非数据进入方法区就如永久代的名字一样永久存在了。这个区域的内存回收目标主要是针对常量池的回收和对类型的卸载,圾回收机制的条件是相当苛刻的。
这段程序,以指数级不断的生成新的字符串,这样可以比较快速的消耗内存。
可以通过异常信息看到出现堆内存的异常,在jdk1.8之后,字符串常量池由永久代(方法区)转移到堆中
jvm的内存参数的设置
- -Xms设置堆的最小空间大小。
- -Xmx设置堆的最大空间大小。
- -XX:NewSize设置新生代最小空间大小。
- -XX:MaxNewSize设置新生代最大空间大小。
- -XX:PermSize设置永久代最小空间大小。
- -XX:MaxPermSize设置永久代最大空间大小。
- -Xss设置每个线程的堆栈大小
6.字符串的比较
对于字符串的比较使用的是== 经过分析可以知道:==比较的是两个字符串对象的引用的地址是否相同。
比较字符串的内容相同:
-
-
boolean
equals(Object anObject)
将此字符串与指定对象进行比较。boolean
equalsIgnoreCase(String anotherString)
将此String
与其他String
比较,忽略案例注意事项。
-
如果要比较两个字符串的内容是否相同 则使用equals方法
System.out.println(s1.equals(s3));
System.out.println(s4.equals(s6));
System.out.println(s1.equals(s7));//false
System.out.println(s1.equals(s8));//false
System.out.println(s1.equalsIgnoreCase(s7));//true
System.out.println(s1.equalsIgnoreCase(s8));//true
需求:模拟完成用户登录
已知用户名和密码,请用程序来实现用户登录,总共有三次机会,登录之后要给出相应的提示。
import java.util.Scanner;
public class LoginTest {
public static void main(String[] args) {
// 假设用户名和密码是已知的 分别为
// String username = "lanqiao";
// String password = "123456";
//用户登录时 用户名和密码有用户从键盘输入 用户最多可尝试三次
for(int i = 0 ; i < 3 ;i++){
// 从键盘录入用户名和密码
Scanner sc = new Scanner(System.in);
System.out.println("请输入您的用户名:");
String inName= sc.nextLine();
inName=null;
System.out.println("请输入您的密码:");
String inPassword = sc.nextLine();
inPassword=null;
//比较用户输入的用户名和密码是否与已知的用户名密码一致
// 当进行字符串和一个变量比较的时候 ,一定要将字符串常量放在equals的前边 这样可以有效的避免空指针异常的出现
if("lanqiao".equals(inName)&&"123456".equals(inPassword)){
System.out.println("登录成功");
break;
}else{
if(2 - i == 0){
System.out.println("您的账户已经被锁定,请联系管理员");
}else{
System.out.println("用户名或密码有误,登录失败,您还有"+(2-i)+"次机会!");
}
}
}
}
}
遍历字符串
从键盘录入一个字符串,使用程序实现在控制台遍历该字符串。
public static void main(String[] args) {
//从键盘输入一个字符串
Scanner sc = new Scanner(System.in);
System.out.println("请输入您要遍历的字符串:");
String str = sc.nextLine();
// 获取数组的长度 使用数组的length属性 获取字符串的长度 使用的是String类的length方法
for(int i = 0 ; i < str.length();i++){
char c = str.charAt(i);
System.out.println(c);
}
}
统计字符的次数
键盘录入一个字符串,统计该字符串中大写字母 小写字母 数字出现的次数 (不考虑其他字符)
对于一个字符判断他是大写字母 小写字母 数字 直接判断:
大写字母: ch >= ‘A’ && ch<=‘Z’
小写字母:ch>=‘a’&& ch<=‘z’
数字:ch>=‘0’&&ch<=‘9’
public class CountCharNum {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入您要统计的字符串:");
String str = sc.nextLine();
// 定义字符统计的变量
int bigCount = 0;
int smallCount = 0 ;
int numberCount=0;
for(int i =0 ; i < str.length();i++){
char ch = str.charAt(i);
if(ch >='A'&& ch<='Z'){
bigCount++;
}else if(ch >='a'&& ch<='z'){
smallCount++;
}else if(ch >='0'&& ch<='9'){
numberCount++;
}
}
System.out.println("大写字母的个数为:" + bigCount);
System.out.println("小写字母的个数为:" + smallCount);
System.out.println("数字的个数为:" + numberCount);
}
}
字符串反转:
定义一个字符串,实现字符串的反转 从控制台输入abc 输出结果为cba
7 String类的常用方法
-
-
char
charAt(int index)
返回char
指定索引处的值。
-
-
-
String
concat(String str)
将指定的字符串连接到该字符串的末尾。
-
-
-
boolean
endsWith(String suffix)
测试此字符串是否以指定的后缀结尾。
-
-
-
boolean
equals(Object anObject)
将此字符串与指定对象进行比较。boolean
equalsIgnoreCase(String anotherString)
将此String
与其他String
比较,忽略案例注意事项。
-
-
-
byte[]
getBytes()
使用平台的默认字符集将此String
编码为字节序列,将结果存储到新的字节数组中。
-
-
-
int
indexOf(int ch)
返回指定字符第一次出现的字符串内的索引。
-
-
-
int
indexOf(int ch, int fromIndex)
返回指定字符第一次出现的字符串内的索引,以指定的索引开始搜索。
-
-
-
int
indexOf(String str)
返回指定子字符串第一次出现的字符串内的索引。
-
-
-
int
indexOf(String str, int fromIndex)
返回指定子串的第一次出现的字符串中的索引,从指定的索引开始。
-
-
-
String
intern()
返回字符串对象的规范表示。
-
-
-
boolean
isEmpty()
返回true
如果,且仅当length()
为0
。
-
-
-
int
lastIndexOf(int ch)
返回指定字符的最后一次出现的字符串中的索引。int
lastIndexOf(int ch, int fromIndex)
返回指定字符的最后一次出现的字符串中的索引,从指定的索引开始向后搜索。int
lastIndexOf(String str)
返回指定子字符串最后一次出现的字符串中的索引。int
lastIndexOf(String str, int fromIndex)
返回指定子字符串的最后一次出现的字符串中的索引,从指定索引开始向后搜索。int
length()
返回此字符串的长度。
-
public static void main(String[] args) {
String str = "abdcdadcd";
System.out.println(str.indexOf("a"));//0
System.out.println(str.lastIndexOf("a"));//5
}
-
-
String
replace(char oldChar, char newChar)
返回从替换所有出现的导致一个字符串oldChar
在此字符串newChar
。
-
-
-
boolean
startsWith(String prefix)
测试此字符串是否以指定的前缀开头。
-
-
-
char[]
toCharArray()
将此字符串转换为新的字符数组。String
toLowerCase()
将所有在此字符String
使用默认语言环境的规则,以小写。
-
-
-
String
toUpperCase()
将所有在此字符String
使用默认语言环境的规则大写。
-
-
-
String
substring(int beginIndex)
返回一个字符串,该字符串是此字符串的子字符串。
-
-
-
String
substring(int beginIndex, int endIndex)
返回一个字符串,该字符串是此字符串的子字符串。
-
-
-
static String
valueOf(boolean b)
返回boolean
参数的字符串boolean
形式。static String
valueOf(char c)
返回char
参数的字符串char
形式。static String
valueOf(char[] data)
返回char
数组参数的字符串char
形式。static String
valueOf(char[] data, int offset, int count)
返回char
数组参数的特定子阵列的字符串char
形式。static String
valueOf(double d)
返回double
参数的字符串double
形式。static String
valueOf(float f)
返回float
参数的字符串float
形式。static String
valueOf(int i)
返回int
参数的字符串int
形式。static String
valueOf(long l)
返回long
参数的字符串long
形式。static String
valueOf(Object obj)
返回Object
参数的字符串Object
形式。
-
String s = String.valueOf(123);//将整数123 转换为字符串123
8 String的常见陷阱
String str1 = "abc";
String str2 = new String("abc");
字符串常量存储在字符串常量池中,并且是共享
字符串非常量对象 存储在堆中
public static void main(String[] args) {
String s1 = "hello";
String s2 = "world";
String s3 = "hello" +"world";
String s4 = s1 +"world";
String s5 = s1 + s2;
String s6 = (s1 + s2).intern();
String s7 = "helloworld";
System.out.println(s3==s4);// false
System.out.println(s3 == s7);//true
System.out.println(s3==s5);//false
System.out.println(s4==s5);//false
System.out.println(s3==s6);//true
}
-
常量与常量的拼接,结果在常量池 且常量池中不会存在相同内容的常量
-
只要其中有一个是变量,结果就在堆中。
-
如果拼接的结果调用intern(),结果就在常量池中
public static void main(String[] args) {
String str1 = new StringBuilder("计算机").append("软件开发").toString();
System.out.println(str1.intern()== str1);//true
String str2 = new StringBuilder("ja").append("va").toString();//java是一个特殊的字符串 在jdk的底层,存在很多“java”字符串
System.out.println(str2.intern()==str2);//false
}