首页 > 数据库 >Android学习笔记八(JAVA):数据库与Room持久性库,菜单栏,数据绑定

Android学习笔记八(JAVA):数据库与Room持久性库,菜单栏,数据绑定

时间:2022-09-02 19:46:45浏览次数:90  
标签:Note JAVA Room 菜单栏 note import java public NoteRepository

本篇笔记实现如下所示的功能。在NoteListFragment页面增加了菜单栏,菜单栏中有New Note选项,点击它跳转到新建Note页面。输入TITLE和CONTENT后,点击CREATE按钮,会在数据库中添加一条数据。返回NoteListFragment,可以看见这条数据。

1.实现菜单栏

2.使用数据绑定实现CreateNoteActivity

3.数据库与Room库

 

1.实现菜单栏

先来一个菜单栏的布局文件,在res文件夹处右键,New->Android Resource File,在Resource type中选择Menu,File name为fragment_note_list.xml。可见,在res下面多了一个menu文件夹。现在我们有两个名为fragment_note_list的布局文件,不过没问题,它们在不同的文件夹中。strings.xml中新增了一个字符串:

<string name="btn_create_note">Create</string>

menu/fragment_note_list.xml代码清单:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/new_note"
        android:title="@string/new_note_menu"
        app:showAsAction="withText"/>
</menu>

注意showAsAction的属性现在给的是“withText"。

现在回到NoteListFragment.java文件,在这里把刚才新建的菜单栏布局添加进去。《Android权威编程指南》(不管是第四版还是第五版)里面用的都是在onCreate()方法中添加setHasOptionsMenu(true)和重写onCreateOptionsMenu()的方式添加菜单栏。但是这种方式已经显示为废弃了,如下图所示。嗯,就是这样,本来用的很习惯的方法,突然间就废弃了。不过废弃也表示进步,肯定是有更好的方法了。(在此想表达,有问题一定要以官方文档为主,也要实时关注官方文档)

根据文档,现在可以使用MenuProvider来给activity提供Menu。来看看这个MenuProvider是啥。哦,原来是个接口。

那就让NoteListFragment.java实现MenuProvider接口吧:

public class NoteListFragment extends Fragment implements MenuProvider{...}

这时会有提示,实现MenuProvider接口需要实现onCreateMenu(Menu,MenuInflater)方法。可以按照提示,按下alt+enter,Android Studio会自动填充这个方法。

@Override
    public void onCreateMenu(@NonNull Menu menu, @NonNull MenuInflater menuInflater) {
        menuInflater.inflate(R.menu.fragment_note_list,menu);
    }

    @Override
    public boolean onMenuItemSelected(@NonNull MenuItem menuItem) {
        switch (menuItem.getItemId()){
            case R.id.new_note:
                jumpToCreateNote();
                return true;
            default:
                return true;
        }
    }

jumpToCreateNote()就是要跳转到新建Note的Activity。现在我们还没有办法实现这个方法,因为还没有新建Note的Activity呢。那么现在可以先把jumpToCreateNote()注释掉,Run app,看看屏幕上是不是已经有菜单栏了呢。答案是没有,因为还没有addMenuProvider()。Holy crap!

回到官方文档,看看咋回事。哦,想要添加菜单栏,需要让MenuHost托管MenuProvider,而Fragment并没有实现MenuHost接口,Component Activity才实现了MenuHost接口。OK了,在NoteListFragment.java的onCreateView()中让Fragment的托管Activity托管MenuProvider就可以了。

@Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
        super.onCreateView(inflater,container,savedInstanceState);
        View view=inflater.inflate(R.layout.fragment_note_list,container,false);
        ActionBar actionBar=((AppCompatActivity) getActivity()).getSupportActionBar();
        actionBar.setTitle(TAG);
getActivity().addMenuProvider(this);
mNoteRecyclerView=(RecyclerView) view.findViewById(R.id.note_recycler_view); mNoteRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); return view; }

现在Run app,再看看,哇,菜单栏出现了。还记得吗,menu/fragment_note_list.xml中showAsAction属性值是“withText",现在把它换成"ifRoom"和”ifRoom|withText"再看看菜单栏是什么样的。

 

2.使用数据绑定实现CreateNoteActivity

笔记三中提到了视图绑定,笔记五中提到了如何创建并启动新的Activity。现在来看一下数据绑定(Data Binding)(ps:本人之前用WPF的时候,真的超爱数据绑定,尤其是双向数据绑定,不要太方便哦,个人认为,双向数据绑定是MVVM最大的特点)。与使用视图绑定一样,使用数据绑定,需要先在build.gradle(module)中添加:

    dataBinding{
        enabled=true
    }

新建一个名为CreateNoteActivity的empty activity。所谓数据绑定,简单点理解就是把模型的数据直接绑定在视图布局上,像下面这样。<layout>节点下面有一个<data>子节点,指定了视图布局中需要的数据的名称(note)和类型(Note类)。<LinearLayout>节点也放在<layout>节点下。其中的两个<EditText>子节点中的android:text属性值分别绑定为Note类型的title属性和content属性(虽然我们在Note.java中用getter方法获取title和content的值的,但是这里可以不用写note.getTitle(),直接用note.title就好)。activity_create_note.xml中需要的这个note与CreateNoteActivity.java中的一个note是绑定的关系。这样的话,CreateNoteActivity.java中的那个note的title和content值就可以给到activity_create_note.xml了。

activity_create_note.xml代码清单:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <data>
        <variable name="note" type="com.larissa.android.note.Note"/>
    </data>
    <LinearLayout
        android:orientation="vertical"
        android:layout_margin="16dp"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textStyle="bold"
            android:text="@string/title_label"/>
        <EditText
            android:id="@+id/note_title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="Enter a title of the note"
            android:text="@{note.title}"/>
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/content_label"
            android:textStyle="bold"/>
        <EditText
            android:id="@+id/note_content"
            android:layout_width="match_parent"
            android:layout_height="260dp"
            android:text="@{note.content}"
            android:inputType="textMultiLine" />
        <Button
            android:id="@+id/btn_create_note"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:text="@string/btn_create_note"/>
    </LinearLayout>
</layout>

下面就在CreateNoteActivity.java中指定一个note,与activity_create_note.xml中的绑定。CreateNoteActivity.java代码清单:

public class CreateNoteActivity extends AppCompatActivity {
    private static final String TAG="CreateNoteActivity";private ActivityCreateNoteBinding mActivityCreateNoteBinding;
    private Note mNote;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mActivityCreateNoteBinding=ActivityCreateNoteBinding.inflate(getLayoutInflater());
        setContentView(mActivityCreateNoteBinding.getRoot());
        mNote=new Note();
        mNote.setTitle("Data Binding");
        mNote.setContent("implements data binding.");
        mActivityCreateNoteBinding.setNote(mNote);
    }
}

在CreateNoteActivity.java中添加了mNote,并通过setNote()方法把它绑定给了activity_create_note.xml视图。为了验证数据绑定,我们先把CreateNoteActivity设置为主Activity。ApplicationManifest.xml代码清单如下:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.larissa.android.note">

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.Note"
        tools:targetApi="31">
        <activity
            android:name=".CreateNoteActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".MainActivity"
            android:exported="false">
        </activity>
    </application>

</manifest>
ApplicationManifest.xml

Run app,应该会出现下方界面了。CreateNoteActivity.java中的mNote的属性值在视图中显示出来了。

数据绑定最大的作用是双向绑定,比如<EditText>是可编辑的Text,用户对于视图中的EditText的Text属性值的编辑,需要能被Activity知道才行。双向绑定只需要把XML文件中的@{}改成@={}就可以了。activity_create_note.xml代码清单:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <data>
        <variable name="note" type="com.larissa.android.note.Note"/>
    </data>
    <LinearLayout
        android:orientation="vertical"
        android:layout_margin="16dp"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textStyle="bold"
            android:text="@string/title_label"/>
        <EditText
            android:id="@+id/note_title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="Enter a title of the note"
            android:text="@={note.title}"/>
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/content_label"
            android:textStyle="bold"/>
        <EditText
            android:id="@+id/note_content"
            android:layout_width="match_parent"
            android:layout_height="260dp"
            android:text="@={note.content}"
            android:inputType="textMultiLine" />
        <Button
            android:id="@+id/btn_create_note"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:text="@string/btn_create_note"/>
    </LinearLayout>
</layout>
activity_create_note.xml

完善CreateNoteActivity,我们给它配个ViewModel,以备后用。然后再实现从NoteListFragment中跳转到CreateNoteActivity。新建CreateNoteViewModel.java文件,继承ViewModel类。在NoteListFragment.java中实现jumpToCreateNote()方法。

    private void jumpToCreateNote(){
        Intent intent=new Intent(getActivity(),CreateNoteActivity.class);
        startActivity(intent);
    }

别忘了把ApplicationManifest.xml中的主Activity改回来。Run app,看看是不是能从NoteListFragment的菜单栏跳转到CreateNoteActivity了。

 

3.数据库与Room库

数据库用于存储note数据。Android使用SQLite数据库。这是一款小巧的嵌入式数据库,使用方便、开发简单。如MySQL和SQLServer一样,它们都使用SQL语言(Structured Query Language,结构化查询语言),也许每种数据库的SQL语法会有少许不同,不过大部分都差不多。而且SQL语句很好懂。如果之前完全没有使用过SQL语言的经验,可以先参见我之前的文章:https://www.cnblogs.com/larissa-0464/category/1989319.html

比起直接使用SQLite API,Android Jetpack提供了Room持久性库,它在SQLite上提供了一个抽象层,在充分利用SQLite的强大功能的同时,能够流畅地访问数据库。具体来说,Room具有以下优势:

  • 针对SQL查询的编译时验证
  • 可最大限度减少重复和容易出错的样板代码的方便注解
  • 简化了数据库迁移路径

如果有.NET CORE相关开发经验的话,我感觉Room就相当于是EntityFrameworkCore(EFcore),起到了ORM(object-relational mapping,对象关系映射)的作用。

使用Room,需要添加相应的依赖,在build.gradle(module)中的dependencies节点中输入下面的代码:

   def room_version = "2.4.3"
    implementation "androidx.room:room-runtime:$room_version"
    annotationProcessor "androidx.room:room-compiler:$room_version"

Room有三个主要的组件:

  • 数据实体,用于表示应用的数据库中的表;
  • 数据访问对象(DAO, data access object),提供CRUD方法(create, retrieve, update, delete)。
  • 数据库类,用于保存数据库并作为应用持久性数据底层连接的主要访问点;

现在开始吧,添加数据库。

第一步,把Note类变成数据实体。很简单,给它加个注解:@Entity。如果表名不想用Note的话,就指定一个:@Entity(tableName=your table name)。表需要一个主键(primary key),一般都是id当主键。此外,用@ColumnInfo(name=*)把column的名称改了。变身后的Note.java代码清单如下:

package com.larissa.android.note;

import androidx.annotation.NonNull;
import androidx.databinding.BaseObservable;
import androidx.databinding.Bindable;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.PrimaryKey;

import java.util.Date;
import java.util.UUID;
@Entity
public class Note extends BaseObservable {
    @NonNull
    @PrimaryKey
    @ColumnInfo(name="id")
    private UUID mId;
    @ColumnInfo(name="title")
    private String mTitle;
    @ColumnInfo(name="content")
    private String mContent;
    @ColumnInfo(name="date")
    private Date mDate;
    @ColumnInfo(name="status")
    private String mStatus;

    public Date getDate() {
        return mDate;
    }

    public void setDate(Date date) {
        mDate = date;
    }

    public String getStatus() {
        return mStatus;
    }

    public void setStatus(String status) {
        mStatus = status;
    }

    public String getNoteLog(){
        String noteLog=String.format("%s at %s",mStatus,mDate);
        return noteLog;
    }

    public void setNoteLog(String status){
        mDate=new Date();
        mStatus=status;
    }

    public UUID getId() {
        return mId;
    }

    public void setId(UUID id) {
        mId = id;
    }
    @Bindable
    public String getTitle() {
        return mTitle;
    }

    public void setTitle(String title) {
        mTitle = title;
        notifyPropertyChanged(BR.title);
    }
    @Bindable
    public String getContent() {
        return mContent;
    }

    public void setContent(String content) {
        mContent = content;
        notifyPropertyChanged(BR.content);
    }
}
Note.java

更多地注解请点击(例如忽略一个field,双主键等):https://developer.android.google.cn/training/data-storage/room/defining-data?hl=zh-cn

第二步,定义Note类的数据访问对象。先新建一个名为database的package。在database中,新建名为NoteDao的接口。NoteDao.java代码清单:

package com.larissa.android.note.database;

import androidx.lifecycle.LiveData;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Update;

import com.larissa.android.note.Note;

import java.util.List;

@Dao
public interface NoteDao {
    @Query("SELECT * FROM Note")
    LiveData<List<Note>>getAllNotes();

    @Insert
    void addNote(Note note);

    @Update
    void updateNote(Note note);

}
NoteDao.java

用@Dao注解表明这是一个DAO接口。另外目前NoteDao中定义了CRUD中的C,R,U,还没有D。更多请点击:https://developer.android.google.cn/training/data-storage/room/accessing-data?hl=zh-cn

第三步,实现数据库类。不过在此之前,先来创建一个类型转换器。为啥呢,因为Note中的UUID和Date类型,SQL的数据库不认识阿,所以要把这两个JAVA才有的类型转成数据库认识的String和Long。在database中新建NoteTypeConverters.java。代码清单如下:

package com.larissa.android.note.database;

import androidx.room.TypeConverter;

import java.util.Date;
import java.util.UUID;

public class NoteTypeConverters {
    @TypeConverter
    public Long fromDate(Date date){
        return date.getTime();
    }

    @TypeConverter
    public Date toDate(Long millisSinceEpoch){
        return new Date(millisSinceEpoch);
    }

    @TypeConverter
    public String fromUUID(UUID uuid){
        return uuid.toString();
    }

    @TypeConverter
    public UUID toUUID(String uuid){
        return UUID.fromString(uuid);
    }
}
NoteTypeConverters.java

新建NoteDatabase.java文件,代码清单如下:

package com.larissa.android.note.database;

import androidx.room.Database;
import androidx.room.RoomDatabase;
import androidx.room.TypeConverters;

import com.larissa.android.note.Note;

@Database(entities = {Note.class},version=1,exportSchema = false)
@TypeConverters({NoteTypeConverters.class})
public abstract class NoteDatabase extends RoomDatabase {
    public abstract NoteDao noteDao();
}
NoteDatabase.java

它需要添加@Database注解,并继承RoomDatabase类。

OK,数据库弄好了。下一步就是使用它了。还记得吗,笔记六中生命周期感知型组件那里提到,ViewModel类不负责获取数据,它调用相应的组件来获取数据。一般干这个活的是repository(仓储)。可以这样理解,repository从database中获取数据,不同的viewmodel都过来问repository要数据和更改数据。按照这个需求分析,每个viewmodel并不需要自己实例化一个repository,它们可以share同一个repository。那么repository就可以实现为单例模式了。(更多关于单例模式的内容,请自行百度。另外乍一看单例模式跟静态类有点像,不过还是不同的,它们之间的区别也请自行百度)。

还需要注意的是,为防止查询阻止界面,Room不允许在主线程上访问数据库。线程是一个单一执行序列。单个线程中的代码会逐步执行。所有Android应用的运行都是从主线程开始的。然而,主线程并不是像线程那样的预定执行序列。它处于一个无限循环的运行状态,等着用户或系统触发事件。一旦有事件触发,主线程便执行代码做出响应。主线程上运行着所有更新UI的代码,其中包括响应activity的启动、按钮的点击等不同UI相关事件的代码。事件处理循环让主线程总是按顺序执行。这样,事件就能一件件处理,同时代码也能快速执行,及时响应。目前,我们的代码都在主线程中执行。但是读取数据库是很费时的,并不能立即完成。如果查询数据库在主线程上运行,那么在此期间,除了等待,啥也干不了。因此,Room不允许在主线程上执行任何数据库操作,而是由后台线程完成,后台线程查询完数据库,再告知主线程。这就是异步操作。

具体到Android上,使用JAVA语言的情况下,可以使用RxJava框架的专用返回类型编写异步DAO方法,或者用Jetpack库中的LiveData封装容器类编写异步可观察查询。那么写入数据库呢,可以用Executor,相当于创建一个新线程,在新线程上面运行指定的任务。了解了之后,那就开始吧。

新建NoteRepository.java文件。这里面要有一个数据库类对象,一个DAO对象,一个用于写入数据库的executor。然后它还要是单例模式。NoteRepository.java代码清单:

package com.larissa.android.note;

import android.content.Context;

import androidx.lifecycle.LiveData;
import androidx.room.Room;

import com.larissa.android.note.database.NoteDao;
import com.larissa.android.note.database.NoteDatabase;

import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

public class NoteRepository {
    private static final String DATABASE_NAME="note-database";
    private static NoteRepository sNoteRepository;
    private NoteDatabase noteDatabase;
    private NoteDao noteDao;
    private Executor executor= Executors.newSingleThreadExecutor();

    private NoteRepository(Context context){
        noteDatabase= Room.databaseBuilder(context,NoteDatabase.class,DATABASE_NAME).build();
        noteDao=noteDatabase.noteDao();
    }

    public static NoteRepository getInstance(){
        if(sNoteRepository==null)
            throw new IllegalStateException("NoteRepository must be initialized");
        return sNoteRepository;
    }

    public static void initialize(Context context){
        if(sNoteRepository==null)
            sNoteRepository=new NoteRepository(context);
    }

    public LiveData<List<Note>>getAllNotes(){
        return noteDao.getAllNotes();
    }

    public void updateNote(Note note){
        executor.execute(new Runnable() {
            @Override
            public void run() {
                noteDao.updateNote(note);
            }
        });
    }

    public void addNote(Note note){
        executor.execute(new Runnable() {
            @Override
            public void run() {
                noteDao.addNote(note);
            }
        });
    }
}
NoteRepository.java

其中,下面这些代码用于初始化NoteRepository。NoteRepository类初始化的时候,使用Room.databaseBuilder().build()方法创建数据库实例。

public class NoteRepository {private NoteDatabase noteDatabase;
    private NoteDao noteDao;private NoteRepository(Context context){
        noteDatabase= Room.databaseBuilder(context,NoteDatabase.class,DATABASE_NAME).build();
        noteDao=noteDatabase.noteDao();
    }
}

下面这些则是用来实现单例模式的。这样ViewModel使用NoteRepository的时候,只需要用它的getInstance()静态方法就好了。不过前提是需要先调用initialize()静态方法初始化NoteRepository。

public class NoteRepository {private static NoteRepository sNoteRepository;public static NoteRepository getInstance(){
        if(sNoteRepository==null)
            throw new IllegalStateException("NoteRepository must be initialized");
        return sNoteRepository;
    }

    public static void initialize(Context context){
        if(sNoteRepository==null)
            sNoteRepository=new NoteRepository(context);
    }
}

下面这些是用来封装数据库DAO的。可见,对于查询操作,返回的结果是LiveData<List<Note>>类的。对于插入和更新操作,使用executor的execute方法完成的。executor则是从Executors工厂中选择了newSingleThreadExecutor()。

public class NoteRepository {private Executor executor= Executors.newSingleThreadExecutor();
public LiveData<List<Note>>getAllNotes(){
        return noteDao.getAllNotes();
    }

    public void updateNote(Note note){
        executor.execute(new Runnable() {
            @Override
            public void run() {
                noteDao.updateNote(note);
            }
        });
    }

    public void addNote(Note note){
        executor.execute(new Runnable() {
            @Override
            public void run() {
                noteDao.addNote(note);
            }
        });
    }
}

弄好了repository,下一步就应该更新ViewModel了。NoteListViewModel.java代码清单:

public class NoteListViewModel extends ViewModel {
    private static final String TAG="NoteListViewModel";
    private NoteRepository noteRepository;
    List<Note> mNotes=new ArrayList<>();
    LiveData<List<Note>>notesLiveData;

    public NoteListViewModel(){
        for(int i=0;i<50;i++){
            Note note=new Note();
            note.setTitle(String.format("Note No.%s",i));
            if(i%2==0)
                note.setNoteLog("Created");
            else
                note.setNoteLog("Modified");
            note.setContent(String.format("This is the content of Note No.%s",i));
            mNotes.add(note);
        }
        noteRepository=NoteRepository.getInstance();
        notesLiveData= noteRepository.getAllNotes();

    }
}

CreateNoteViewModel.java代码清单:

public class CreateNoteViewModel extends ViewModel {
    private NoteRepository noteRepository=NoteRepository.getInstance();

    public void createNote(Note note){
        note.setDate(new Date());
        note.setStatus("Created");
        note.setId(UUID.randomUUID());
        noteRepository.addNote(note);
    }

}

增加了创建Note的方法createNote(),现在把这个方法绑定给activity_create_note.xml中的CREATE按钮。CreateNoteActivity.java代码清单:

public class CreateNoteActivity extends AppCompatActivity {
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        mActivityCreateNoteBinding.btnCreateNote.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mCreateNoteViewModel.createNote(mNote);
            }
        });
    }
}

OK,run app,看看是不是大功告成啦。其实这里还有个bud,在NoteListFragment页面导航到NoteFragment页面,再按返回键,返回到NoteListFragment页面后,菜单栏上出现了两个New Node。Holy crap。没办法阿,因为每次调用onCreateView()的时候都添加了addMenuProvider(this)。再看一下官方文档,哦,MenuHost里面还有个removeMenuProvider()的方法。哦了,把这个方法放在onDestroyView()里。这回再试试呢~

源代码见:https://gitee.com/larissaLiu/note_-v3

标签:Note,JAVA,Room,菜单栏,note,import,java,public,NoteRepository
From: https://www.cnblogs.com/larissa-0464/p/16631399.html

相关文章

  • Intellij IDEA 'Error:java: 无效的源发行版:13'
      原文链接:https://blog.csdn.net/liuqiker/article/details/101528205  1,首先亮一下我的IDEA版本:2,我遇上的问题:3,我怎么解决的?1,首先亮一下我的IDEA版本(官网下载......
  • java 内存模型之 volatile 核心原理与应用
    1.happens-before规则https://blog.csdn.net/qq_39935047/article/details/1203847992.Juc12_Volatile的可见性、不保证可见性、有序性、使用、内存屏障四大指令StoreSt......
  • 使用JavaConfig实现配置
    @Configuration在一个类上加了@Configration之后就类似<beans><beanid="xxx" class="xxxxxx"/></beans>这个也会被Spring容器托管,注册到容器中,因为他本来就是一个@......
  • java学习笔记018 反射
    1.java.lang.Class获取Class对象的四种方式//1Classclazz1=Person.class;//2Personp=newPerson();Classclazz2=p.getClass();//3 用得多Classclazz3=......
  • java.lang.reflect.InvocationTargetException
    1.自定义BaseServlet时候,里面的方法应该是重新HttpServlet里的service方法,自己写成dogetdopost了,错误!2.mapper的xml配置文件,查询总数的方法selectTotalCount需要......
  • java实现桌面右下角弹窗效果
    http://www.3qphp.com/java/framework/3542.htmlInfoUtil.javaimportjava.awt.BorderLayout;importjava.awt.Color;importjava.awt.Cursor;importjava.awt.Deskt......
  • 防止JAVA程序重复启动的一个另类解决办法
    http://blog.itpub.net/443058/viewspace-915510/我们项目中有一个后台任务处理程序,是java开发application,用以处理网站提交的一些批量数据文件,因为这些数据文件数据量一......
  • Java 在Word文档中添加艺术字
    艺术字是以普通文字为基础,经过专业的字体设计师艺术加工的变形字体。字体特点符合文字含义、具有美观有趣、易认易识、醒目张扬等特性,是一种有图案意味或装饰意味的字体变......
  • java下载csv乱码问题
    一.问题。用EXCEL打开文件时,总是产生乱码,但是用NOTEPAD++打开时,显示正常。然后,在NOTEPADD++的“格式”工具栏中查了一下文件编码,发现是“以UTF-8格式编码”。二.解决......
  • 【校招VIP】[Java][一本][6分]按照真实的技能点进行业务描述
    关注【校招VIP】公众号,回复【简历】,添加校招顾问微信,即可获取简历指导!本份简历是一位21届一本java同学的简历,简历评分6分。一、学员简历  二、指导意见简历版式问......