Java基础知识回顾之二 ----- 修饰符和String
前言
在上一篇中,回顾了Java的基本数据类型 ,这篇就来回顾下Java中的一些修饰符以及String。
修饰符介绍
Java修饰符主要分为两类:
- 访问修饰符
- 非访问修饰符
其中访问修饰符主要包括 private、default、protected、public。
非访问修饰符主要包括 static、final、abstract、synchronized。
访问修饰符
访问修饰符可以使用下图这张表来说明访问权限:
修饰符 | 当前类 | 同一包内 | 子类 | 其它包 |
---|---|---|---|---|
public | Y | Y | Y | Y |
protected | Y | Y | Y | N |
default | Y | Y | N | N |
private | Y | N | N | N |
简单点查看访问级别的话,级别是由低到高。
private<default<protected<public
private
被private修饰的变量、方法仅限在本类中使用。
所以private是最严的的访问级别,主要用于隐藏类的一些细节实现和保护类的数据。
例如pojo类就是使用private修饰变量,对外提供setter和getter的方法。
还有如果使用private用来修饰构造方法的话,该类是不能实例化的。这种在单例模式中可以经常看到!
虽然private主要用于修饰变量和方法,不过也可以修饰内部类,只不过是内部类。
例如:
public class Test{
//修饰一个私有变量
private int count=1;
//修饰一个私有方法
private int add(int i,int j){
return i+j;
}
private class Test1{
}
}
注意:private不能修饰外部类。
因为Test类中的变量和方法是私有的,所以其他类无法调用!
例:
public class Test2 {
public static void main(String[] args) {
Test t=new Test();
//下面的变量和方法是无法获取的
//t.count=2;
//t.add(1,2);
}
}
说明:其实private修饰的方法和变量是可以使用反射调用,不过这里就不说明了。
default
default:就是不使用任何修饰符。类、接口、变量、方法都可以使用。不过仅限在同一包下。
例如:
class Test{
int count=1;
int add(int i,int j){
return i+j;
}
interface Test1{
}
}
protected
被protected修饰的变量、方法仅仅对同一包内的类和所有子类可见。
例如:
public class Test{
protected int count=1;
protected int add(int i,int j){
return i+j;
}
protected class Test1{
}
}
在同包下可以直接调用,如果不在同包,则需要继承才可以使用。
public class Test2 extends Test{
public static void main(String[] args) {
Test t=new Test();
t.count=2;
t.add(1,2);
}
}
注意:protected不能修饰外部类。
public
public:修饰的类、接口、变量、方法对所有类都可以使用。
例如:
public class Test{
public int count=1;
public int add(int i,int j){
return i+j;
}
}
非访问修饰符
为了实现一些其他的功能,Java 也提供了许多非访问修饰符。
static
static: 用来修饰类变量和类方法。
静态变量:
static在修饰类变量的时候,无论该类被实例化了多少次,它的静态变量只有一份拷贝。静态变量也被称为类变量。局部变量是不能被声明为static变量的。
静态方法:
static在修饰类方法的时候,静态方法是不能使用类的非静态变量。静态方法可以直接通过类名调用,因此静态方法中是不能用this和super关键字的。
示例:
public class Test{
public String name="xuwujing";
public static String name2="xuwujing";
public static String getName() {
//这个一句 会报错 因为静态方法是不能使用类的非静态变量
//String reult=name;
//这一句就可以
String reult=name2;
return reult;
}
//main方法是静态方法,可以直接调用本类中的静态方法和静态变量
public static void main(String[] args) {
System.out.println(name2);
System.out.println(getName());
}
//该方法是不静态方法,所以调用本类中的静态方法和静态变量时,
//需要使用classname.variablename和 classname.methodname的方式访问
private void print(){
System.out.println(Test.name2);
System.out.println(Test.getName());
}
}
在这里顺便提一下,static 静态块。
在JVM类加载机制中,如果类存在直接的父类并且这个类还没有被初始化,那么就先初始化父类;如果类中存在初始化语句,就依次执行这些初始化语句。
可能上述的两句话不太好理解,那么这里我们来运行下代码查看其结果,通过结果可能就能更好的理解上述语句的话了。
示例:
class HelloA {
public HelloA() {
System.out.println("HelloA");
}
{ System.out.println("I'm A class"); }
static { System.out.println("static A"); }
}
public class HelloB extends HelloA{
public HelloB() {
System.out.println("HelloB");
}
{ System.out.println("I'm B class"); }
static { System.out.println("static B"); }
public static void main(String[] args) {
new HelloB();
}
结果:
static A
static B
I'm A class
HelloA
I'm B class
HelloB
那么根据这个类返回的结果是不是感觉更好理解了呢?
创建对象时构造器的调用顺序是:
先初始化静态成员,然后调用父类构造器,再初始化非静态成员,最后调用自身构造器。
那么static修饰符这块的运用可以总结如下:
- 静态变量在内存中只有一个拷贝,在类的所有实例中共享。
- 在静态方法中不能直接访问实例方法和实例变量,反之可以。
- 在静态方法中不能使用this和super关键字。
- 静态方法不能被abstract修饰。
- 静态方法和静态变量都可以通过类名直接访问。
- 当类被加载时,静态代码块只被加载一次。有多个静态变量或块时,按声明顺序加载。
final
final :用来修饰类、方法和变量。
final 修饰的类不能够被继承,修饰的方法不能被继承类重新定义,修饰的变量为常量,是不可修改的。
如果上述语句不好理解的话,我们可以通过编写相关代码进行实验。
定义一个final修饰的变量、方法以及类。然后进行相关的测试
示例:
public class Test{
//定义一个final修饰的变量
public static final String name="xuwujing";
public static void main(String[] args) {
//这句会报错 因为该变量已经被final修饰了
name="张三";
}
//类加上final之后,该类是无法被继承的
final class Test2{
}
//这句会报错,因为Test2是被final修饰的类
class Test3 extends Test2{
}
class Test4{
//定义一个被final修饰的方法
final Date getTime(){
return new Date();
}
}
class Test5 extends Test4{
//这句会报错,因为final方法是不能被子类修改的。
Date getTime(){
return new Date();
}
}
}
从上述 代码结果,我们可以得出一下结论:
final修饰类:表示该类不能被继承;
final修饰方法:表示方法不能被重写;
final修饰变量:表示变量只能一次赋值以后值不能被修改(常量);
abstract
abstract :用来创建抽象类和抽象方法。
Java是面向对象的语言,而抽象类是Java语言中对抽象概念进行定义的一种机制,也正是因为这个,所以赋予了Java强大的面向对象的能力。
修饰类
会使这个类成为一个抽象类,这个类将不能生成对象实例,但可以做为对象变量声明的类型(见后面实例),也就是编译时类型。抽象类就相当于一类的半成品,需要子类继承并覆盖其中的抽象方法。
修饰方法
会使这个方法变成抽象方法,也就是只有声明而没有实现,需要子类继承实现。
这里依旧使用一个简单例子来进行理解。
public class AbstractTest{
public static void main(String[] args) {
//这句会报错,因为抽象类不能实例化
// Animal a=new Animal();
//抽象类可以实例化重写该类抽象方法的子类
Animal a = new Dog();
a.show();
}
}
abstract class Animal{
abstract void show();
public void print(){
System.out.println("Animal");
}
}
//继承抽象类需要实现抽象类的方法
class Dog extends Animal{
@Override
void show() {
System.out.println("This is Dog!");
}
}
总结:
1、抽象类和抽象方法都需要被abstract修饰。抽象方法一定要定义在抽象类中。
2、抽象类不可以创建实例,原因:调用抽象方法没有意义。
3、只有覆盖了抽象类中所有的抽象方法后,其子类才可以实例化。否则该子类还是一个抽象类。
注意事项:
1、抽象类不能用来实例化对象,声明抽象类的唯一目的是为了将来对该类进行扩充。 2、一个类不能同时被 abstract 和 final
修饰。如果一个类包含抽象方法,那么该类一定要声明为抽象类,否则将出现编译错误。
3、抽象方法是一种没有任何实现的方法,该方法的的具体实现由子类提供。 4、抽象方法不能被声明成 final 和 static。
5、任何继承抽象类的子类必须实现父类的所有抽象方法,除非该子类也是抽象类。
6、如果一个类包含若干个抽象方法,那么该类必须声明为抽象类。抽象类可以不包含抽象方法。
synchronized
synchronized: 修饰的方法同一时间只能被一个线程访问。在多线程中运用很常见。
synchronized 的解释如下:
synchronized 方法控制对类成员变量的访问:每个类实例对应一把锁,每个 synchronized方法都必须获得调用该方法的类实例的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。这种机制确保了同一时刻对于每一个类实例,其所有声明synchronized 的成员函数中至多只有一个处于可执行状态(因为至多只有一个能够获得该类实例对应的锁),从而有效避免了类成员变量的访问冲突(只要所有可能访问类成员变量的方法均被声明为 synchronized)。
简单的来说,就是使用synchronized 修饰的方法,在多线程进行同时访问的时候,只会让一个线程先进行访问,其它的线程等候,当这个线程访问完了之后,再让下一个进行访问,依次类推。
Java中还有两个不太常见的修饰符,transient 和native。
transient:被 transient 修饰的实例变量时,java 虚拟机(JVM)跳过该特定的变量。
native: 被native修饰的方法实际是由另一种语言进行实现的本地方法。例如Java中获取的Long类型的时间戳 :System.currentTimeMillis();
实际是由native 修饰的,
源码为:
public static native long currentTimeMillis();
String
String 类型可能就是我们最常用的的对象了。
首先说明,String并不是基本数据类型,而是一个对象,并且是不可变的对象。查看源码可以String类是被final修饰的,是不可被继承的!
String的在未被初始化的时候为null,表示它还没有被创建,自然也就没有分配空间;
而" "和 new String()不是null,它们是已经被创建,只是值为空而已!并且也分配了内存空间。
String有15种构造方法,有两种是过时的,其中包含char[],byte[],int[],String,StringBuffer,StringBuilder。
我们在创建String对象的的时候,一般是使用 String str="xxx",但有时也会用new String()来初始话字符串。
例如:
String hello="hello";
String newHello=new String("hello");
char []cHello ={'h','e','l','l','o'};
String str=new String(cHello);
注意:String 类是不可改变的,所以你一旦创建了 String 对象,那它的值就无法改变了。
String常用方法
大概讲述了String的用法之后,这里我们来列举一些String常用的方法。
1.length :返回此字符串的长度。
2.charAt:返回指定索引处的 char 值。
3.compareTo:把这个字符串和另一个对象比较。
4.concat:将指定字符串连接到此字符串的结尾。
5.split:根据给定正则表达式的匹配拆分此字符串。
6.equals:将此字符串与指定的对象比较。
7.endsWith:测试此字符串是否以指定的后缀结束。
8.startsWith:测试此字符串是否以指定的前缀结束。
9.getBytes: 使用平台的默认字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中。
10.indexOf:返回指定字符在此字符串中第一次出现处的索引。
11.replace:返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。 12:substring:返回一个新的字符串,它是此字符串的一个子字符串。
...
更多可以参考Api文档。
String对象比较
String作为我们最常用的对象,在面试中估计也会接触不少。一般来说,会考到String的常量池相关问题,主要是使用String进行比较的时候,==和equals这两种方法来判断是否相当。这里收集了一些String经常遇到的问题。
代码如下:
String s1 = "test";
String s2 = new String("test");
String s3 = "te";
String s4 = "st";
String s5 = "te" + "st";
String s6 = s3 + s4;
String s7 = new String(s1);
System.out.println(s1 == s2);
System.out.println(s1 == s5);
System.out.println(s1 == s6);
System.out.println(s7==s1);
System.out.println(s7.equals(s1));
结果:
false
true
false
false
true
如果有经验的话,大概可以一眼看出结果。但是如果经验不足的话,往往会吃这个亏。这里来解释下为什么会出现这种结果。
1.虽然看起来是一样的,但是新建一个String类的时候会重新分配引用地址,而 == 就是比较引用地址,所以为false。
2.在编译之前就可以确认s5=test, 并且引用地址一样,所以为true;
3.字符串常量池的原则 这时 s6 的值是在运行时得到的,它会重新构造字符串对象 所以为false。
4.和第一个一样的,就是换汤不换药,所以为false。
5.equals 只比较值相等,不关心它的引用地址。
看完上面的例子之后,再来看看下面的这个
代码示例:
String ab="ab";
String c="c";
String ab_c=ab+c;
String ab_c1="ab"+"c";
String abc="abc";
System.out.println(ab_c == abc + " : " + ab_c.equals(abc));
System.out.println((ab_c == abc) + " : " + ab_c.equals(abc));
System.out.println((ab_c1 == abc) + " : " + ab_c1.equals(abc));
运行结果:
false
false : true
true : true
到这里,可能就会诧异了,为什么和我想的不一样呢?
这里其实是有陷阱的,也就是运算符的优先级。
第一个结果就是优先级的问题导致的,它会先计算 abc + " : " + ab_c.equals(abc)
,然后再来进行比较,所以为false。同理,下面的也是如此,基本和上面的那个例子差不多,这里就不再概述了。
String、StringBuffer和StringBuilder
String、StringBuffer和StringBuilder的区别:
- String: String的特点是一旦赋值,便不能更改其指向的字符对象,如果更改,则会指向一个新的字符对象。
- StringBuffer:StringBuffer对象可以调用其方法动态的进行增加、插入、修改和删 除操作,且不用像数组那样事先指定大小,从而实现多次插入字 符,一次整体取出的效果,因而操作字符串非常灵活方便。并且生成数据之后可以toString转为String,线程安全。
- StringBuilder:它是在单线程环境下使用的,因为它的所有方面都没有被synchronized修饰,因此它的效率也比StringBuffer要高。
关于字符串拼接方式,在String类中,我们最常用的是 + ,其次是使用StringBuffer或StringBuilder 的append方法,至于String类中的concat几乎很少用到。
一般来说,如果在少量的字符串进行拼接的话,我们会使用+,如果拼接过多的话,单线程使用 StringBuilder ,多线程使用StringBuffer 进行拼接。因为使用String 的 + 在过多的字符串进行拼接的时候会极大的使用内存,因为它在凭借的时候还是使用 append()方法,然后再进行toString转换,如果是少量的时候,是感觉不到差异的,但是在大量拼接的时候就会明显感受得到。
代码示例:
String str="Hello World";
String str1="";
StringBuffer sbr=new StringBuffer(str);
StringBuilder sbd=new StringBuilder(str);
long start=System.currentTimeMillis();
for(int i=0;i<10000;i++){
str1+=str;
}
System.out.println("String累加用时:"+(System.currentTimeMillis()-start)+"ms");
long start2=System.currentTimeMillis();
for(int i=0;i<10000;i++){
sbr.append(str);
}
System.out.println("StringBuffer累加用时:"+(System.currentTimeMillis()-start2)+"ms");
long start3=System.currentTimeMillis();
for(int i=0;i<10000;i++){
sbd.append(str);
}
System.out.println("StringBuilder累加用时:"+(System.currentTimeMillis()-start3)+"ms");
结果:
String累加用时:701ms
StringBuffer累加用时:2ms
StringBuilder累加用时:0ms
这里从输出结果中可以看到String 的+拼接方法的耗时了。但是使用 + 实在是方便。所以在这里建议如果字符串拼接次数在10一下,可以使用+,过多的则用StringBuffer或StringBuilder。
其它
参考:
https://blog.csdn.net/qiumengchen12/article/details/44939929
https://blog.csdn.net/chenssy/article/details/13004291
到此,本文就结束了,谢谢阅读!欢迎留言和点赞,你的支持是我写作最大的动力!
版权声明:
作者:虚无境
博客园出处:http://www.cnblogs.com/xuwujing
CSDN出处:http://blog.csdn.net/qazwsxpcm
个人博客出处:http://www.panchengming.com
原创不易,转载请标明出处,谢谢!
Java基础知识回顾之二 ----- 修饰符和String的更多相关文章
- Java基础知识回顾之七 ----- 总结篇
前言 在之前Java基础知识回顾中,我们回顾了基础数据类型.修饰符和String.三大特性.集合.多线程和IO.本篇文章则对之前学过的知识进行总结.除了简单的复习之外,还会增加一些相应的理解. 基础数 ...
- java基础知识回顾之---java String final类普通方法
辞职了,最近一段时间在找工作,把在大二的时候学习java基础知识回顾下,拿出来跟大家分享,如果有问题,欢迎大家的指正. /* * 按照面向对象的思想对字符串进行功能分类. * ...
- java基础知识一览(二)
一.java基础知识 1.一个文件中只能有一个public的类,因为他的类名要求和文件名相同. 2.classpath变量可以设置其它目录下的类. 例如:类文件所在目录是:F:\Javajdk,那么没 ...
- Java基础知识回顾(一):字符串小结
Java的基础知识回顾之字符串 一.引言 很多人喜欢在前面加入赘述,事实上去技术网站找相关的内容的一般都应当已经对相应知识有一定了解,因此我不再过多赘述字符串到底是什么东西,在官网中已经写得很明确了, ...
- Java基础知识回顾之三 ----- 封装、继承和多态
前言 在上一篇中回顾了java的修饰符和String类,这篇就来回顾下Java的三大特性:封装.继承.多态. 封装 什么是封装 在面向对象程式设计方法中,封装是指一种将抽象性函式接口的实现细节部份包装 ...
- Java基础(三):修饰符、运算符、循环结构和分支结构
一.Java修饰符: Java语言提供了很多修饰符,主要分为以下两类:访问修饰符和非访问修饰符.修饰符用来定义类.方法或者变量,通常放在语句的最前端. 1.访问控制修饰符: Java中,可以使用访问控 ...
- java基础知识总结(二)
Java中的代码块 java中的代码块是用{}括起来的代码,进行一些功能的限定 静态代码块:在类第一次被初始化的是后执行,负责一些类的初始化操作,仅仅只执行一次 构造代码块:顾名思义,辅助构造器进行初 ...
- java基础知识回顾之接口
/* abstract class AbsDemo { abstract void show1(); abstract void show2(); } 当一个抽象类中的方法都是抽象的时候,这时可以将该 ...
- Java基础知识回顾之一 ----- 基本数据类型
前言 在开始工作至今,学习各种各样的技术之中发现自己的很多Java的基础知识都忘了⊙﹏⊙b汗... 而且越是学习越是发现Java基础的重要性,所以准备单独抽一下时间进行Java基础的重新学习.在重新学 ...
随机推荐
- hbase存储优化
1.上面的2张图主要说明hbase的存储特点 (1).每个值(每条记录的每一个列的值)的存储,都完整的存储了rowkey.column family.column.版本(时间戳),以及该列的值. 这样 ...
- Ansible自动化运维笔记1(安装配置)
1.Ansible的安装 pip install ansible==1.9.1 ansible1.9.1版本依赖的软件有 Python2.6以上版本 paramiko模块 PyYAML Jinja2 ...
- javascript类型判断方法
判断javascript中的类型,共有四种常用的方法 var a=6; var b="str"; var c=true; var arr=[]; typeof 用于基本类型的判断 ...
- 用Node.JS+MongoDB搭建个人博客(万众期待的router.js)(四)
万众期待的router.js,是我现在最想写的一个博客.因为他包含了整个个人博客的精髓.在这里,所有的请求配置,返回的参数等等所做的业务逻辑都在这个文件里实现. 我会详细说明这些代码的作用,所以这篇博 ...
- HighCharts之2D堆面积图
HighCharts之2D堆面积图 1.HighCharts之2D堆面积图源码 StackedArea.html: <!DOCTYPE html> <html> <hea ...
- Netty的并发编程实践3:CAS指令和原子类
互斥同步最主要的问题就是进行线程阻塞和唤醒所带来的性能的额外损耗,因此这种同步被称为阻塞同步,它属于一种悲观的并发策略,我们称之为悲观锁.随着硬件和操作系统指令集的发展和优化,产生了非阻塞同步,被称为 ...
- 创建数据库表默认字段封装SQL
declare @Table_Name varchar(500) declare @strSQL varchar(500) set @Table_Name='UserInfo' --在此处设置要创建的 ...
- 关于vue如何解决数据渲染完成之前,dom树显示问题
在id="app"以下的标签中添加属性v-cloak 并且在css文件中添加[v-cloak]{display:none} 如果效果失效,这种原因是有几种可能,游览器大的解析加载速 ...
- mybatis的动态增删改查
1.动态SQL片段 通过SQL片段达到代码复用 <!-- 动态条件分页查询 --> <sql id="sql_count"> select count(*) ...
- 同一张表省市县sql查询
一,表的结构 SELECT * FROM t_unionpay_areacode t SELECT * FROM t_unionpay_areacode t WHERE t.`name`LIKE &q ...